AccountService.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. namespace App\Services;
  3. use Cache;
  4. use App\Profile;
  5. use App\Status;
  6. use App\User;
  7. use App\UserSetting;
  8. use App\Models\UserDomainBlock;
  9. use App\Transformer\Api\AccountTransformer;
  10. use League\Fractal;
  11. use League\Fractal\Serializer\ArraySerializer;
  12. use Illuminate\Support\Facades\DB;
  13. use Illuminate\Support\Str;
  14. use \NumberFormatter;
  15. class AccountService
  16. {
  17. const CACHE_KEY = 'pf:services:account:';
  18. public static function get($id, $softFail = false)
  19. {
  20. $res = Cache::remember(self::CACHE_KEY . $id, 43200, function() use($id) {
  21. $fractal = new Fractal\Manager();
  22. $fractal->setSerializer(new ArraySerializer());
  23. $profile = Profile::find($id);
  24. if(!$profile || $profile->status === 'delete') {
  25. return null;
  26. }
  27. $resource = new Fractal\Resource\Item($profile, new AccountTransformer());
  28. return $fractal->createData($resource)->toArray();
  29. });
  30. if(!$res) {
  31. return $softFail ? null : abort(404);
  32. }
  33. return $res;
  34. }
  35. public static function getMastodon($id, $softFail = false)
  36. {
  37. $account = self::get($id, $softFail);
  38. if(!$account) {
  39. return null;
  40. }
  41. if(config('exp.emc') == false) {
  42. return $account;
  43. }
  44. unset(
  45. $account['header_bg'],
  46. $account['is_admin'],
  47. $account['last_fetched_at'],
  48. $account['local'],
  49. $account['location'],
  50. $account['note_text'],
  51. $account['pronouns'],
  52. $account['website']
  53. );
  54. $account['avatar_static'] = $account['avatar'];
  55. $account['bot'] = false;
  56. $account['emojis'] = [];
  57. $account['fields'] = [];
  58. $account['header'] = url('/storage/headers/missing.png');
  59. $account['header_static'] = url('/storage/headers/missing.png');
  60. $account['last_status_at'] = null;
  61. return $account;
  62. }
  63. public static function del($id)
  64. {
  65. Cache::forget('pf:activitypub:user-object:by-id:' . $id);
  66. return Cache::forget(self::CACHE_KEY . $id);
  67. }
  68. public static function settings($id)
  69. {
  70. return Cache::remember('profile:compose:settings:' . $id, 604800, function() use($id) {
  71. $settings = UserSetting::whereUserId($id)->first();
  72. if(!$settings) {
  73. return self::defaultSettings();
  74. }
  75. return collect($settings)
  76. ->filter(function($item, $key) {
  77. return in_array($key, array_keys(self::defaultSettings())) == true;
  78. })
  79. ->map(function($item, $key) {
  80. if($key == 'compose_settings') {
  81. $cs = self::defaultSettings()['compose_settings'];
  82. $ms = is_array($item) ? $item : [];
  83. return array_merge($cs, $ms);
  84. }
  85. if($key == 'other') {
  86. $other = self::defaultSettings()['other'];
  87. $mo = is_array($item) ? $item : [];
  88. return array_merge($other, $mo);
  89. }
  90. return $item;
  91. });
  92. });
  93. }
  94. public static function canEmbed($id)
  95. {
  96. return self::settings($id)['other']['disable_embeds'] == false;
  97. }
  98. public static function defaultSettings()
  99. {
  100. return [
  101. 'crawlable' => true,
  102. 'public_dm' => false,
  103. 'reduce_motion' => false,
  104. 'high_contrast_mode' => false,
  105. 'video_autoplay' => false,
  106. 'show_profile_follower_count' => true,
  107. 'show_profile_following_count' => true,
  108. 'compose_settings' => [
  109. 'default_scope' => 'public',
  110. 'default_license' => 1,
  111. 'media_descriptions' => false
  112. ],
  113. 'other' => [
  114. 'advanced_atom' => false,
  115. 'disable_embeds' => false,
  116. 'mutual_mention_notifications' => false,
  117. 'hide_collections' => false,
  118. 'hide_like_counts' => false,
  119. 'hide_groups' => false,
  120. 'hide_stories' => false,
  121. 'disable_cw' => false,
  122. ]
  123. ];
  124. }
  125. public static function syncPostCount($id)
  126. {
  127. $profile = Profile::find($id);
  128. if(!$profile) {
  129. return false;
  130. }
  131. $key = self::CACHE_KEY . 'pcs:' . $id;
  132. if(Cache::has($key)) {
  133. return;
  134. }
  135. $count = Status::whereProfileId($id)
  136. ->whereNull('in_reply_to_id')
  137. ->whereNull('reblog_of_id')
  138. ->whereIn('scope', ['public', 'unlisted', 'private'])
  139. ->count();
  140. $profile->status_count = $count;
  141. $profile->save();
  142. Cache::put($key, 1, 900);
  143. return true;
  144. }
  145. public static function usernameToId($username)
  146. {
  147. $key = self::CACHE_KEY . 'u2id:' . hash('sha256', $username);
  148. return Cache::remember($key, 14400, function() use($username) {
  149. $s = Str::of($username);
  150. if($s->contains('@') && !$s->startsWith('@')) {
  151. $username = "@{$username}";
  152. }
  153. $profile = DB::table('profiles')
  154. ->whereUsername($username)
  155. ->first();
  156. if(!$profile) {
  157. return null;
  158. }
  159. return (string) $profile->id;
  160. });
  161. }
  162. public static function hiddenFollowers($id)
  163. {
  164. $account = self::get($id, true);
  165. if(!$account || !isset($account['local']) || $account['local'] == false) {
  166. return false;
  167. }
  168. return Cache::remember('pf:acct:settings:hidden-followers:' . $id, 43200, function() use($id) {
  169. $user = User::whereProfileId($id)->first();
  170. if(!$user) {
  171. return false;
  172. }
  173. $settings = UserSetting::whereUserId($user->id)->first();
  174. if($settings) {
  175. return $settings->show_profile_follower_count == false;
  176. }
  177. return false;
  178. });
  179. }
  180. public static function hiddenFollowing($id)
  181. {
  182. $account = self::get($id, true);
  183. if(!$account || !isset($account['local']) || $account['local'] == false) {
  184. return false;
  185. }
  186. return Cache::remember('pf:acct:settings:hidden-following:' . $id, 43200, function() use($id) {
  187. $user = User::whereProfileId($id)->first();
  188. if(!$user) {
  189. return false;
  190. }
  191. $settings = UserSetting::whereUserId($user->id)->first();
  192. if($settings) {
  193. return $settings->show_profile_following_count == false;
  194. }
  195. return false;
  196. });
  197. }
  198. public static function setLastActive($id = false)
  199. {
  200. if(!$id) { return; }
  201. $key = 'user:last_active_at:id:' . $id;
  202. if(!Cache::has($key)) {
  203. $user = User::find($id);
  204. if(!$user) { return; }
  205. $user->last_active_at = now();
  206. $user->save();
  207. Cache::put($key, 1, 14400);
  208. }
  209. return;
  210. }
  211. public static function blocksDomain($pid, $domain = false)
  212. {
  213. if(!$domain) {
  214. return;
  215. }
  216. return UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->exists();
  217. }
  218. public static function formatNumber($num) {
  219. if(!$num || $num < 1) {
  220. return "0";
  221. }
  222. $num = intval($num);
  223. $formatter = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
  224. $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 1);
  225. if ($num >= 1000000000) {
  226. return $formatter->format($num / 1000000000) . 'B';
  227. } else if ($num >= 1000000) {
  228. return $formatter->format($num / 1000000) . 'M';
  229. } elseif ($num >= 1000) {
  230. return $formatter->format($num / 1000) . 'K';
  231. } else {
  232. return $formatter->format($num);
  233. }
  234. }
  235. public static function getMetaDescription($id)
  236. {
  237. $account = self::get($id, true);
  238. if(!$account) return "";
  239. $posts = self::formatNumber($account['statuses_count']) . ' Posts, ';
  240. $following = self::formatNumber($account['following_count']) . ' Following, ';
  241. $followers = self::formatNumber($account['followers_count']) . ' Followers';
  242. $note = $account['note'] && strlen($account['note']) ?
  243. ' · ' . \Purify::clean(strip_tags(str_replace("\n", '', str_replace("\r", '', $account['note'])))) :
  244. '';
  245. return $posts . $following . $followers . $note;
  246. }
  247. }