DirectMessageController.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\DirectMessage;
  4. use App\Jobs\DirectPipeline\DirectDeletePipeline;
  5. use App\Jobs\DirectPipeline\DirectDeliverPipeline;
  6. use App\Jobs\StatusPipeline\StatusDelete;
  7. use App\Media;
  8. use App\Models\Conversation;
  9. use App\Notification;
  10. use App\Profile;
  11. use App\Services\AccountService;
  12. use App\Services\MediaBlocklistService;
  13. use App\Services\MediaPathService;
  14. use App\Services\MediaService;
  15. use App\Services\StatusService;
  16. use App\Services\UserFilterService;
  17. use App\Services\UserRoleService;
  18. use App\Services\UserStorageService;
  19. use App\Services\WebfingerService;
  20. use App\Status;
  21. use App\UserFilter;
  22. use App\Util\ActivityPub\Helpers;
  23. use App\Util\Lexer\Autolink;
  24. use Illuminate\Http\Request;
  25. use Illuminate\Support\Str;
  26. class DirectMessageController extends Controller
  27. {
  28. public function __construct()
  29. {
  30. $this->middleware('auth');
  31. }
  32. public function browse(Request $request)
  33. {
  34. $this->validate($request, [
  35. 'a' => 'nullable|string|in:inbox,sent,filtered',
  36. 'page' => 'nullable|integer|min:1|max:99',
  37. ]);
  38. $user = $request->user();
  39. if ($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id)) {
  40. return [];
  41. }
  42. $profile = $user->profile_id;
  43. $action = $request->input('a', 'inbox');
  44. $page = $request->input('page', 1);
  45. $limit = 8;
  46. $offset = ($page - 1) * $limit;
  47. $baseQuery = DirectMessage::select(
  48. 'id', 'type', 'to_id', 'from_id', 'status_id',
  49. 'is_hidden', 'meta', 'created_at', 'read_at'
  50. )->with(['author', 'status', 'recipient']);
  51. if (config('database.default') == 'pgsql') {
  52. $query = match ($action) {
  53. 'inbox' => $baseQuery->whereToId($profile)
  54. ->whereIsHidden(false)
  55. ->orderBy('created_at', 'desc'),
  56. 'sent' => $baseQuery->whereFromId($profile)
  57. ->orderBy('created_at', 'desc'),
  58. 'filtered' => $baseQuery->whereToId($profile)
  59. ->whereIsHidden(true)
  60. ->orderBy('created_at', 'desc'),
  61. default => throw new \InvalidArgumentException('Invalid action')
  62. };
  63. $dms = $query->offset($offset)
  64. ->limit($limit)
  65. ->get();
  66. $dms = $action === 'sent' ?
  67. $dms->unique('to_id') :
  68. $dms->unique('from_id');
  69. } else {
  70. $query = match ($action) {
  71. 'inbox' => $baseQuery->whereToId($profile)
  72. ->whereIsHidden(false)
  73. ->groupBy('from_id', 'id', 'type', 'to_id', 'status_id',
  74. 'is_hidden', 'meta', 'created_at', 'read_at')
  75. ->orderBy('created_at', 'desc'),
  76. 'sent' => $baseQuery->whereFromId($profile)
  77. ->groupBy('to_id', 'id', 'type', 'from_id', 'status_id',
  78. 'is_hidden', 'meta', 'created_at', 'read_at')
  79. ->orderBy('created_at', 'desc'),
  80. 'filtered' => $baseQuery->whereToId($profile)
  81. ->whereIsHidden(true)
  82. ->groupBy('from_id', 'id', 'type', 'to_id', 'status_id',
  83. 'is_hidden', 'meta', 'created_at', 'read_at')
  84. ->orderBy('created_at', 'desc'),
  85. default => throw new \InvalidArgumentException('Invalid action')
  86. };
  87. $dms = $query->offset($offset)
  88. ->limit($limit)
  89. ->get();
  90. }
  91. $mappedDms = $dms->map(function ($r) use ($action) {
  92. if ($action === 'sent') {
  93. return [
  94. 'id' => (string) $r->to_id,
  95. 'name' => $r->recipient->name,
  96. 'username' => $r->recipient->username,
  97. 'avatar' => $r->recipient->avatarUrl(),
  98. 'url' => $r->recipient->url(),
  99. 'isLocal' => (bool) ! $r->recipient->domain,
  100. 'domain' => $r->recipient->domain,
  101. 'timeAgo' => $r->created_at->diffForHumans(null, true, true),
  102. 'lastMessage' => $r->status->caption,
  103. 'messages' => [],
  104. ];
  105. }
  106. return [
  107. 'id' => (string) $r->from_id,
  108. 'name' => $r->author->name,
  109. 'username' => $r->author->username,
  110. 'avatar' => $r->author->avatarUrl(),
  111. 'url' => $r->author->url(),
  112. 'isLocal' => (bool) ! $r->author->domain,
  113. 'domain' => $r->author->domain,
  114. 'timeAgo' => $r->created_at->diffForHumans(null, true, true),
  115. 'lastMessage' => $r->status->caption,
  116. 'messages' => [],
  117. ];
  118. });
  119. return response()->json($mappedDms->values());
  120. }
  121. public function create(Request $request)
  122. {
  123. $this->validate($request, [
  124. 'to_id' => 'required',
  125. 'message' => 'required|string|min:1|max:500',
  126. 'type' => 'required|in:text,emoji',
  127. ]);
  128. $user = $request->user();
  129. abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
  130. if (! $user->is_admin) {
  131. if ((bool) ! config_cache('instance.allow_new_account_dms')) {
  132. abort_if($user->created_at->gt(now()->subHours(72)), 400, 'You need to wait a bit before you can DM another account');
  133. }
  134. }
  135. $profile = $user->profile;
  136. $recipient = Profile::where('id', '!=', $profile->id)->findOrFail($request->input('to_id'));
  137. abort_if(in_array($profile->id, $recipient->blockedIds()->toArray()), 403);
  138. $msg = $request->input('message');
  139. if ((! $recipient->domain && $recipient->user->settings->public_dm == false) || $recipient->is_private) {
  140. if ($recipient->follows($profile) == true) {
  141. $hidden = false;
  142. } else {
  143. $hidden = true;
  144. }
  145. } else {
  146. $hidden = false;
  147. }
  148. $status = new Status;
  149. $status->profile_id = $profile->id;
  150. $status->caption = $msg;
  151. $status->visibility = 'direct';
  152. $status->scope = 'direct';
  153. $status->in_reply_to_profile_id = $recipient->id;
  154. $status->save();
  155. $dm = new DirectMessage;
  156. $dm->to_id = $recipient->id;
  157. $dm->from_id = $profile->id;
  158. $dm->status_id = $status->id;
  159. $dm->is_hidden = $hidden;
  160. $dm->type = $request->input('type');
  161. $dm->save();
  162. Conversation::updateOrInsert(
  163. [
  164. 'to_id' => $recipient->id,
  165. 'from_id' => $profile->id,
  166. ],
  167. [
  168. 'type' => $dm->type,
  169. 'status_id' => $status->id,
  170. 'dm_id' => $dm->id,
  171. 'is_hidden' => $hidden,
  172. ]
  173. );
  174. if (filter_var($msg, FILTER_VALIDATE_URL)) {
  175. if (Helpers::validateUrl($msg)) {
  176. $dm->type = 'link';
  177. $dm->meta = [
  178. 'domain' => parse_url($msg, PHP_URL_HOST),
  179. 'local' => parse_url($msg, PHP_URL_HOST) ==
  180. parse_url(config('app.url'), PHP_URL_HOST),
  181. ];
  182. $dm->save();
  183. }
  184. }
  185. $nf = UserFilter::whereUserId($recipient->id)
  186. ->whereFilterableId($profile->id)
  187. ->whereFilterableType('App\Profile')
  188. ->whereFilterType('dm.mute')
  189. ->exists();
  190. if ($recipient->domain == null && $hidden == false && ! $nf) {
  191. $notification = new Notification;
  192. $notification->profile_id = $recipient->id;
  193. $notification->actor_id = $profile->id;
  194. $notification->action = 'dm';
  195. $notification->item_id = $dm->id;
  196. $notification->item_type = "App\DirectMessage";
  197. $notification->save();
  198. }
  199. if ($recipient->domain) {
  200. $this->remoteDeliver($dm);
  201. }
  202. $res = [
  203. 'id' => (string) $dm->id,
  204. 'isAuthor' => $profile->id == $dm->from_id,
  205. 'reportId' => (string) $dm->status_id,
  206. 'hidden' => (bool) $dm->is_hidden,
  207. 'type' => $dm->type,
  208. 'text' => $dm->status->caption,
  209. 'media' => null,
  210. 'timeAgo' => $dm->created_at->diffForHumans(null, null, true),
  211. 'seen' => $dm->read_at != null,
  212. 'meta' => $dm->meta,
  213. ];
  214. return response()->json($res);
  215. }
  216. public function thread(Request $request)
  217. {
  218. $this->validate($request, [
  219. 'pid' => 'required',
  220. 'max_id' => 'sometimes|integer',
  221. 'min_id' => 'sometimes|integer',
  222. ]);
  223. $user = $request->user();
  224. abort_if(
  225. $user->has_roles && ! UserRoleService::can('can-direct-message', $user->id),
  226. 403,
  227. 'Invalid permissions for this action'
  228. );
  229. $uid = $user->profile_id;
  230. $pid = $request->input('pid');
  231. $max_id = $request->input('max_id');
  232. $min_id = $request->input('min_id');
  233. $profile = Profile::findOrFail($pid);
  234. $query = DirectMessage::select(
  235. 'id',
  236. 'is_hidden',
  237. 'from_id',
  238. 'to_id',
  239. 'type',
  240. 'status_id',
  241. 'meta',
  242. 'created_at',
  243. 'read_at'
  244. )->with(['status']);
  245. if ($min_id) {
  246. $res = $query->where('id', '>', $min_id)
  247. ->where(function ($query) use ($pid, $uid) {
  248. $query->where('from_id', $pid)->where('to_id', $uid);
  249. })->orWhere(function ($query) use ($pid, $uid) {
  250. $query->where('from_id', $uid)->where('to_id', $pid);
  251. })
  252. ->orderBy('id', 'asc')
  253. ->take(8)
  254. ->get()
  255. ->reverse();
  256. } elseif ($max_id) {
  257. $res = $query->where('id', '<', $max_id)
  258. ->where(function ($query) use ($pid, $uid) {
  259. $query->where('from_id', $pid)->where('to_id', $uid);
  260. })->orWhere(function ($query) use ($pid, $uid) {
  261. $query->where('from_id', $uid)->where('to_id', $pid);
  262. })
  263. ->orderBy('id', 'desc')
  264. ->take(8)
  265. ->get();
  266. } else {
  267. $res = $query->where(function ($query) use ($pid, $uid) {
  268. $query->where('from_id', $pid)->where('to_id', $uid);
  269. })->orWhere(function ($query) use ($pid, $uid) {
  270. $query->where('from_id', $uid)->where('to_id', $pid);
  271. })
  272. ->orderBy('id', 'desc')
  273. ->take(8)
  274. ->get();
  275. }
  276. $messages = $res->filter(function ($message) {
  277. return $message && $message->status;
  278. })->map(function ($message) use ($uid) {
  279. return [
  280. 'id' => (string) $message->id,
  281. 'hidden' => (bool) $message->is_hidden,
  282. 'isAuthor' => $uid == $message->from_id,
  283. 'type' => $message->type,
  284. 'text' => $message->status->caption,
  285. 'media' => $message->status->firstMedia() ? $message->status->firstMedia()->url() : null,
  286. 'carousel' => MediaService::get($message->status_id),
  287. 'created_at' => $message->created_at->format('c'),
  288. 'timeAgo' => $message->created_at->diffForHumans(null, null, true),
  289. 'seen' => $message->read_at != null,
  290. 'reportId' => (string) $message->status_id,
  291. 'meta' => is_string($message->meta) ? json_decode($message->meta, true) : $message->meta,
  292. ];
  293. })->values();
  294. $filters = UserFilterService::mutes($uid);
  295. return response()->json([
  296. 'id' => (string) $profile->id,
  297. 'name' => $profile->name,
  298. 'username' => $profile->username,
  299. 'avatar' => $profile->avatarUrl(),
  300. 'url' => $profile->url(),
  301. 'muted' => in_array($profile->id, $filters),
  302. 'isLocal' => (bool) ! $profile->domain,
  303. 'domain' => $profile->domain,
  304. 'created_at' => $profile->created_at->format('c'),
  305. 'updated_at' => $profile->updated_at->format('c'),
  306. 'timeAgo' => $profile->created_at->diffForHumans(null, true, true),
  307. 'lastMessage' => '',
  308. 'messages' => $messages,
  309. ], 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
  310. }
  311. public function delete(Request $request)
  312. {
  313. $this->validate($request, [
  314. 'id' => 'required',
  315. ]);
  316. $sid = $request->input('id');
  317. $pid = $request->user()->profile_id;
  318. $dm = DirectMessage::whereFromId($pid)
  319. ->whereStatusId($sid)
  320. ->firstOrFail();
  321. $status = Status::whereProfileId($pid)
  322. ->findOrFail($dm->status_id);
  323. $recipient = AccountService::get($dm->to_id);
  324. if (! $recipient) {
  325. return response('', 422);
  326. }
  327. if ($recipient['local'] == false) {
  328. $dmc = $dm;
  329. $this->remoteDelete($dmc);
  330. } else {
  331. StatusDelete::dispatch($status)->onQueue('high');
  332. }
  333. if (Conversation::whereStatusId($sid)->count()) {
  334. $latest = DirectMessage::where(['from_id' => $dm->from_id, 'to_id' => $dm->to_id])
  335. ->orWhere(['to_id' => $dm->from_id, 'from_id' => $dm->to_id])
  336. ->latest()
  337. ->first();
  338. if ($latest->status_id == $sid) {
  339. Conversation::where(['to_id' => $dm->from_id, 'from_id' => $dm->to_id])
  340. ->update([
  341. 'updated_at' => $latest->updated_at,
  342. 'status_id' => $latest->status_id,
  343. 'type' => $latest->type,
  344. 'is_hidden' => false,
  345. ]);
  346. Conversation::where(['to_id' => $dm->to_id, 'from_id' => $dm->from_id])
  347. ->update([
  348. 'updated_at' => $latest->updated_at,
  349. 'status_id' => $latest->status_id,
  350. 'type' => $latest->type,
  351. 'is_hidden' => false,
  352. ]);
  353. } else {
  354. Conversation::where([
  355. 'status_id' => $sid,
  356. 'to_id' => $dm->from_id,
  357. 'from_id' => $dm->to_id,
  358. ])->delete();
  359. Conversation::where([
  360. 'status_id' => $sid,
  361. 'from_id' => $dm->from_id,
  362. 'to_id' => $dm->to_id,
  363. ])->delete();
  364. }
  365. }
  366. StatusService::del($status->id, true);
  367. $status->forceDeleteQuietly();
  368. return [200];
  369. }
  370. public function get(Request $request, $id)
  371. {
  372. $user = $request->user();
  373. abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
  374. $pid = $request->user()->profile_id;
  375. $dm = DirectMessage::whereStatusId($id)->firstOrFail();
  376. abort_if($pid !== $dm->to_id && $pid !== $dm->from_id, 404);
  377. return response()->json($dm, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
  378. }
  379. public function mediaUpload(Request $request)
  380. {
  381. $this->validate($request, [
  382. 'file' => function () {
  383. return [
  384. 'required',
  385. 'mimetypes:'.config_cache('pixelfed.media_types'),
  386. 'max:'.config_cache('pixelfed.max_photo_size'),
  387. ];
  388. },
  389. 'to_id' => 'required',
  390. ]);
  391. $user = $request->user();
  392. abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
  393. $profile = $user->profile;
  394. $recipient = Profile::where('id', '!=', $profile->id)->findOrFail($request->input('to_id'));
  395. abort_if(in_array($profile->id, $recipient->blockedIds()->toArray()), 403);
  396. if ((! $recipient->domain && $recipient->user->settings->public_dm == false) || $recipient->is_private) {
  397. if ($recipient->follows($profile) == true) {
  398. $hidden = false;
  399. } else {
  400. $hidden = true;
  401. }
  402. } else {
  403. $hidden = false;
  404. }
  405. $accountSize = UserStorageService::get($user->id);
  406. abort_if($accountSize === -1, 403, 'Invalid request.');
  407. $photo = $request->file('file');
  408. $fileSize = $photo->getSize();
  409. $sizeInKbs = (int) ceil($fileSize / 1000);
  410. $updatedAccountSize = (int) $accountSize + (int) $sizeInKbs;
  411. if ((bool) config_cache('pixelfed.enforce_account_limit') == true) {
  412. $limit = (int) config_cache('pixelfed.max_account_size');
  413. if ($updatedAccountSize >= $limit) {
  414. abort(403, 'Account size limit reached.');
  415. }
  416. }
  417. $mimes = explode(',', config_cache('pixelfed.media_types'));
  418. if (in_array($photo->getMimeType(), $mimes) == false) {
  419. abort(403, 'Invalid or unsupported mime type.');
  420. }
  421. $storagePath = MediaPathService::get($user, 2).Str::random(8);
  422. $path = $photo->storePublicly($storagePath);
  423. $hash = \hash_file('sha256', $photo);
  424. abort_if(MediaBlocklistService::exists($hash) == true, 451);
  425. $status = new Status;
  426. $status->profile_id = $profile->id;
  427. $status->caption = null;
  428. $status->visibility = 'direct';
  429. $status->scope = 'direct';
  430. $status->in_reply_to_profile_id = $recipient->id;
  431. $status->save();
  432. $media = new Media;
  433. $media->status_id = $status->id;
  434. $media->profile_id = $profile->id;
  435. $media->user_id = $user->id;
  436. $media->media_path = $path;
  437. $media->original_sha256 = $hash;
  438. $media->size = $photo->getSize();
  439. $media->mime = $photo->getMimeType();
  440. $media->caption = null;
  441. $media->filter_class = null;
  442. $media->filter_name = null;
  443. $media->save();
  444. $dm = new DirectMessage;
  445. $dm->to_id = $recipient->id;
  446. $dm->from_id = $profile->id;
  447. $dm->status_id = $status->id;
  448. $dm->type = array_first(explode('/', $media->mime)) == 'video' ? 'video' : 'photo';
  449. $dm->is_hidden = $hidden;
  450. $dm->save();
  451. Conversation::updateOrInsert(
  452. [
  453. 'to_id' => $recipient->id,
  454. 'from_id' => $profile->id,
  455. ],
  456. [
  457. 'type' => $dm->type,
  458. 'status_id' => $status->id,
  459. 'dm_id' => $dm->id,
  460. 'is_hidden' => $hidden,
  461. ]
  462. );
  463. $user->storage_used = (int) $updatedAccountSize;
  464. $user->storage_used_updated_at = now();
  465. $user->save();
  466. if ($recipient->domain) {
  467. $this->remoteDeliver($dm);
  468. }
  469. return [
  470. 'id' => $dm->id,
  471. 'reportId' => (string) $dm->status_id,
  472. 'type' => $dm->type,
  473. 'url' => $media->url(),
  474. ];
  475. }
  476. public function composeLookup(Request $request)
  477. {
  478. $this->validate($request, [
  479. 'q' => 'required|string|min:2|max:50',
  480. 'remote' => 'nullable',
  481. ]);
  482. $user = $request->user();
  483. if ($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id)) {
  484. return [];
  485. }
  486. $q = $request->input('q');
  487. $r = $request->input('remote', false);
  488. if ($r && ! Str::of($q)->contains('.')) {
  489. return [];
  490. }
  491. if ($r && Helpers::validateUrl($q)) {
  492. Helpers::profileFetch($q);
  493. }
  494. if (Str::of($q)->startsWith('@')) {
  495. if (strlen($q) < 3) {
  496. return [];
  497. }
  498. if (substr_count($q, '@') == 2) {
  499. WebfingerService::lookup($q);
  500. }
  501. $q = mb_substr($q, 1);
  502. }
  503. $blocked = UserFilter::whereFilterableType('App\Profile')
  504. ->whereFilterType('block')
  505. ->whereFilterableId($request->user()->profile_id)
  506. ->pluck('user_id');
  507. $blocked->push($request->user()->profile_id);
  508. $results = Profile::select('id', 'domain', 'username')
  509. ->whereNotIn('id', $blocked)
  510. ->where('username', 'like', '%'.$q.'%')
  511. ->orderBy('domain')
  512. ->limit(8)
  513. ->get()
  514. ->map(function ($r) {
  515. $acct = AccountService::get($r->id);
  516. return [
  517. 'local' => (bool) ! $r->domain,
  518. 'id' => (string) $r->id,
  519. 'name' => $r->username,
  520. 'privacy' => true,
  521. 'avatar' => $r->avatarUrl(),
  522. 'account' => $acct,
  523. ];
  524. });
  525. return $results;
  526. }
  527. public function read(Request $request)
  528. {
  529. $this->validate($request, [
  530. 'pid' => 'required',
  531. 'sid' => 'required',
  532. ]);
  533. $pid = $request->input('pid');
  534. $sid = $request->input('sid');
  535. $user = $request->user();
  536. abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
  537. $dms = DirectMessage::whereToId($request->user()->profile_id)
  538. ->whereFromId($pid)
  539. ->where('status_id', '>=', $sid)
  540. ->get();
  541. $now = now();
  542. foreach ($dms as $dm) {
  543. $dm->read_at = $now;
  544. $dm->save();
  545. }
  546. return response()->json($dms->pluck('id'));
  547. }
  548. public function mute(Request $request)
  549. {
  550. $this->validate($request, [
  551. 'id' => 'required',
  552. ]);
  553. $user = $request->user();
  554. abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
  555. $fid = $request->input('id');
  556. $pid = $request->user()->profile_id;
  557. UserFilter::firstOrCreate(
  558. [
  559. 'user_id' => $pid,
  560. 'filterable_id' => $fid,
  561. 'filterable_type' => 'App\Profile',
  562. 'filter_type' => 'dm.mute',
  563. ]
  564. );
  565. return [200];
  566. }
  567. public function unmute(Request $request)
  568. {
  569. $this->validate($request, [
  570. 'id' => 'required',
  571. ]);
  572. $user = $request->user();
  573. abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
  574. $fid = $request->input('id');
  575. $pid = $request->user()->profile_id;
  576. $f = UserFilter::whereUserId($pid)
  577. ->whereFilterableId($fid)
  578. ->whereFilterableType('App\Profile')
  579. ->whereFilterType('dm.mute')
  580. ->firstOrFail();
  581. $f->delete();
  582. return [200];
  583. }
  584. public function remoteDeliver($dm)
  585. {
  586. $profile = $dm->author;
  587. $url = $dm->recipient->sharedInbox ?? $dm->recipient->inbox_url;
  588. $status = $dm->status;
  589. if (! $status) {
  590. return;
  591. }
  592. $tags = [
  593. [
  594. 'type' => 'Mention',
  595. 'href' => $dm->recipient->permalink(),
  596. 'name' => $dm->recipient->emailUrl(),
  597. ],
  598. ];
  599. $content = $status->caption ? Autolink::create()->autolink($status->caption) : null;
  600. $body = [
  601. '@context' => [
  602. 'https://w3id.org/security/v1',
  603. 'https://www.w3.org/ns/activitystreams',
  604. ],
  605. 'id' => $dm->status->permalink(),
  606. 'type' => 'Create',
  607. 'actor' => $dm->status->profile->permalink(),
  608. 'published' => $dm->status->created_at->toAtomString(),
  609. 'to' => [$dm->recipient->permalink()],
  610. 'cc' => [],
  611. 'object' => [
  612. 'id' => $dm->status->url(),
  613. 'type' => 'Note',
  614. 'summary' => null,
  615. 'content' => $content,
  616. 'inReplyTo' => null,
  617. 'published' => $dm->status->created_at->toAtomString(),
  618. 'url' => $dm->status->url(),
  619. 'attributedTo' => $dm->status->profile->permalink(),
  620. 'to' => [$dm->recipient->permalink()],
  621. 'cc' => [],
  622. 'sensitive' => (bool) $dm->status->is_nsfw,
  623. 'attachment' => $dm->status->media()->orderBy('order')->get()->map(function ($media) {
  624. return [
  625. 'type' => $media->activityVerb(),
  626. 'mediaType' => $media->mime,
  627. 'url' => $media->url(),
  628. 'name' => $media->caption,
  629. ];
  630. })->toArray(),
  631. 'tag' => $tags,
  632. ],
  633. ];
  634. DirectDeliverPipeline::dispatch($profile, $url, $body)->onQueue('high');
  635. }
  636. public function remoteDelete($dm)
  637. {
  638. $profile = $dm->author;
  639. $url = $dm->recipient->sharedInbox ?? $dm->recipient->inbox_url;
  640. $body = [
  641. '@context' => [
  642. 'https://www.w3.org/ns/activitystreams',
  643. ],
  644. 'id' => $dm->status->permalink('#delete'),
  645. 'to' => [
  646. 'https://www.w3.org/ns/activitystreams#Public',
  647. ],
  648. 'type' => 'Delete',
  649. 'actor' => $dm->status->profile->permalink(),
  650. 'object' => [
  651. 'id' => $dm->status->url(),
  652. 'type' => 'Tombstone',
  653. ],
  654. ];
  655. DirectDeletePipeline::dispatch($profile, $url, $body)->onQueue('high');
  656. }
  657. }