DirectMessageController.php 26 KB

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