DirectMessageController.php 26 KB

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