]> Untitled Git - lemmy.git/blob - api_tests/src/shared.ts
add enable_federated_downvotes site option
[lemmy.git] / api_tests / src / shared.ts
1 import {
2   GetReplies,
3   GetRepliesResponse,
4   GetUnreadCount,
5   GetUnreadCountResponse,
6   LemmyHttp,
7   LocalUser,
8 } from "lemmy-js-client";
9 import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
10 import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
11 import { EditPost } from "lemmy-js-client/dist/types/EditPost";
12 import { EditSite } from "lemmy-js-client/dist/types/EditSite";
13 import { FeaturePost } from "lemmy-js-client/dist/types/FeaturePost";
14 import { GetComments } from "lemmy-js-client/dist/types/GetComments";
15 import { GetCommentsResponse } from "lemmy-js-client/dist/types/GetCommentsResponse";
16 import { GetPost } from "lemmy-js-client/dist/types/GetPost";
17 import { GetPostResponse } from "lemmy-js-client/dist/types/GetPostResponse";
18 import { LockPost } from "lemmy-js-client/dist/types/LockPost";
19 import { Login } from "lemmy-js-client/dist/types/Login";
20 import { Post } from "lemmy-js-client/dist/types/Post";
21 import { PostResponse } from "lemmy-js-client/dist/types/PostResponse";
22 import { RemovePost } from "lemmy-js-client/dist/types/RemovePost";
23 import { ResolveObject } from "lemmy-js-client/dist/types/ResolveObject";
24 import { ResolveObjectResponse } from "lemmy-js-client/dist/types/ResolveObjectResponse";
25 import { Search } from "lemmy-js-client/dist/types/Search";
26 import { SearchResponse } from "lemmy-js-client/dist/types/SearchResponse";
27 import { Comment } from "lemmy-js-client/dist/types/Comment";
28 import { BanPersonResponse } from "lemmy-js-client/dist/types/BanPersonResponse";
29 import { BanPerson } from "lemmy-js-client/dist/types/BanPerson";
30 import { BanFromCommunityResponse } from "lemmy-js-client/dist/types/BanFromCommunityResponse";
31 import { BanFromCommunity } from "lemmy-js-client/dist/types/BanFromCommunity";
32 import { CommunityResponse } from "lemmy-js-client/dist/types/CommunityResponse";
33 import { FollowCommunity } from "lemmy-js-client/dist/types/FollowCommunity";
34 import { CreatePostLike } from "lemmy-js-client/dist/types/CreatePostLike";
35 import { CommentResponse } from "lemmy-js-client/dist/types/CommentResponse";
36 import { CreateComment } from "lemmy-js-client/dist/types/CreateComment";
37 import { EditComment } from "lemmy-js-client/dist/types/EditComment";
38 import { DeleteComment } from "lemmy-js-client/dist/types/DeleteComment";
39 import { RemoveComment } from "lemmy-js-client/dist/types/RemoveComment";
40 import { GetPersonMentionsResponse } from "lemmy-js-client/dist/types/GetPersonMentionsResponse";
41 import { GetPersonMentions } from "lemmy-js-client/dist/types/GetPersonMentions";
42 import { CreateCommentLike } from "lemmy-js-client/dist/types/CreateCommentLike";
43 import { CreateCommunity } from "lemmy-js-client/dist/types/CreateCommunity";
44 import { GetCommunity } from "lemmy-js-client/dist/types/GetCommunity";
45 import { DeleteCommunity } from "lemmy-js-client/dist/types/DeleteCommunity";
46 import { RemoveCommunity } from "lemmy-js-client/dist/types/RemoveCommunity";
47 import { PrivateMessageResponse } from "lemmy-js-client/dist/types/PrivateMessageResponse";
48 import { CreatePrivateMessage } from "lemmy-js-client/dist/types/CreatePrivateMessage";
49 import { EditPrivateMessage } from "lemmy-js-client/dist/types/EditPrivateMessage";
50 import { DeletePrivateMessage } from "lemmy-js-client/dist/types/DeletePrivateMessage";
51 import { LoginResponse } from "lemmy-js-client/dist/types/LoginResponse";
52 import { Register } from "lemmy-js-client/dist/types/Register";
53 import { SaveUserSettings } from "lemmy-js-client/dist/types/SaveUserSettings";
54 import { DeleteAccount } from "lemmy-js-client/dist/types/DeleteAccount";
55 import { GetSiteResponse } from "lemmy-js-client/dist/types/GetSiteResponse";
56 import { DeleteAccountResponse } from "lemmy-js-client/dist/types/DeleteAccountResponse";
57 import { GetSite } from "lemmy-js-client/dist/types/GetSite";
58 import { PrivateMessagesResponse } from "lemmy-js-client/dist/types/PrivateMessagesResponse";
59 import { GetPrivateMessages } from "lemmy-js-client/dist/types/GetPrivateMessages";
60 import { PostReportResponse } from "lemmy-js-client/dist/types/PostReportResponse";
61 import { CreatePostReport } from "lemmy-js-client/dist/types/CreatePostReport";
62 import { ListPostReportsResponse } from "lemmy-js-client/dist/types/ListPostReportsResponse";
63 import { ListPostReports } from "lemmy-js-client/dist/types/ListPostReports";
64 import { CommentReportResponse } from "lemmy-js-client/dist/types/CommentReportResponse";
65 import { CreateCommentReport } from "lemmy-js-client/dist/types/CreateCommentReport";
66 import { ListCommentReportsResponse } from "lemmy-js-client/dist/types/ListCommentReportsResponse";
67 import { ListCommentReports } from "lemmy-js-client/dist/types/ListCommentReports";
68 import { GetPostsResponse } from "lemmy-js-client/dist/types/GetPostsResponse";
69 import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
70 import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDetailsResponse";
71 import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
72
73 export interface API {
74   client: LemmyHttp;
75   auth: string;
76 }
77
78 export let alpha: API = {
79   client: new LemmyHttp("http://127.0.0.1:8541"),
80   auth: "",
81 };
82
83 export let beta: API = {
84   client: new LemmyHttp("http://127.0.0.1:8551"),
85   auth: "",
86 };
87
88 export let gamma: API = {
89   client: new LemmyHttp("http://127.0.0.1:8561"),
90   auth: "",
91 };
92
93 export let delta: API = {
94   client: new LemmyHttp("http://127.0.0.1:8571"),
95   auth: "",
96 };
97
98 export let epsilon: API = {
99   client: new LemmyHttp("http://127.0.0.1:8581"),
100   auth: "",
101 };
102
103 const password = "lemmylemmy";
104
105 export async function setupLogins() {
106   let formAlpha: Login = {
107     username_or_email: "lemmy_alpha",
108     password,
109   };
110   let resAlpha = alpha.client.login(formAlpha);
111
112   let formBeta: Login = {
113     username_or_email: "lemmy_beta",
114     password,
115   };
116   let resBeta = beta.client.login(formBeta);
117
118   let formGamma: Login = {
119     username_or_email: "lemmy_gamma",
120     password,
121   };
122   let resGamma = gamma.client.login(formGamma);
123
124   let formDelta: Login = {
125     username_or_email: "lemmy_delta",
126     password,
127   };
128   let resDelta = delta.client.login(formDelta);
129
130   let formEpsilon: Login = {
131     username_or_email: "lemmy_epsilon",
132     password,
133   };
134   let resEpsilon = epsilon.client.login(formEpsilon);
135
136   let res = await Promise.all([
137     resAlpha,
138     resBeta,
139     resGamma,
140     resDelta,
141     resEpsilon,
142   ]);
143
144   alpha.auth = res[0].jwt ?? "";
145   beta.auth = res[1].jwt ?? "";
146   gamma.auth = res[2].jwt ?? "";
147   delta.auth = res[3].jwt ?? "";
148   epsilon.auth = res[4].jwt ?? "";
149
150   // Registration applications are now enabled by default, need to disable them
151   let editSiteForm: EditSite = {
152     registration_mode: "Open",
153     rate_limit_message: 999,
154     rate_limit_post: 999,
155     rate_limit_register: 999,
156     rate_limit_image: 999,
157     rate_limit_comment: 999,
158     rate_limit_search: 999,
159     auth: "",
160   };
161
162   // Set the blocks and auths for each
163   editSiteForm.auth = alpha.auth;
164   editSiteForm.allowed_instances = [
165     "lemmy-beta",
166     "lemmy-gamma",
167     "lemmy-delta",
168     "lemmy-epsilon",
169   ];
170   await alpha.client.editSite(editSiteForm);
171
172   editSiteForm.auth = beta.auth;
173   editSiteForm.allowed_instances = [
174     "lemmy-alpha",
175     "lemmy-gamma",
176     "lemmy-delta",
177     "lemmy-epsilon",
178   ];
179   await beta.client.editSite(editSiteForm);
180
181   editSiteForm.auth = gamma.auth;
182   editSiteForm.allowed_instances = [
183     "lemmy-alpha",
184     "lemmy-beta",
185     "lemmy-delta",
186     "lemmy-epsilon",
187   ];
188   await gamma.client.editSite(editSiteForm);
189
190   editSiteForm.allowed_instances = ["lemmy-beta"];
191   editSiteForm.auth = delta.auth;
192   await delta.client.editSite(editSiteForm);
193
194   editSiteForm.auth = epsilon.auth;
195   editSiteForm.allowed_instances = [];
196   editSiteForm.blocked_instances = ["lemmy-alpha"];
197   await epsilon.client.editSite(editSiteForm);
198
199   // Create the main alpha/beta communities
200   // Ignore thrown errors of duplicates
201   try {
202     await createCommunity(alpha, "main");
203     await createCommunity(beta, "main");
204   } catch (_) {}
205 }
206
207 export async function createPost(
208   api: API,
209   community_id: number,
210 ): Promise<PostResponse> {
211   let name = randomString(5);
212   let body = randomString(10);
213   let url = "https://google.com/";
214   let form: CreatePost = {
215     name,
216     url,
217     body,
218     auth: api.auth,
219     community_id,
220   };
221   return api.client.createPost(form);
222 }
223
224 export async function editPost(api: API, post: Post): Promise<PostResponse> {
225   let name = "A jest test federated post, updated";
226   let form: EditPost = {
227     name,
228     post_id: post.id,
229     auth: api.auth,
230   };
231   return api.client.editPost(form);
232 }
233
234 export async function deletePost(
235   api: API,
236   deleted: boolean,
237   post: Post,
238 ): Promise<PostResponse> {
239   let form: DeletePost = {
240     post_id: post.id,
241     deleted: deleted,
242     auth: api.auth,
243   };
244   return api.client.deletePost(form);
245 }
246
247 export async function removePost(
248   api: API,
249   removed: boolean,
250   post: Post,
251 ): Promise<PostResponse> {
252   let form: RemovePost = {
253     post_id: post.id,
254     removed,
255     auth: api.auth,
256   };
257   return api.client.removePost(form);
258 }
259
260 export async function featurePost(
261   api: API,
262   featured: boolean,
263   post: Post,
264 ): Promise<PostResponse> {
265   let form: FeaturePost = {
266     post_id: post.id,
267     featured,
268     feature_type: "Community",
269     auth: api.auth,
270   };
271   return api.client.featurePost(form);
272 }
273
274 export async function lockPost(
275   api: API,
276   locked: boolean,
277   post: Post,
278 ): Promise<PostResponse> {
279   let form: LockPost = {
280     post_id: post.id,
281     locked,
282     auth: api.auth,
283   };
284   return api.client.lockPost(form);
285 }
286
287 export async function resolvePost(
288   api: API,
289   post: Post,
290 ): Promise<ResolveObjectResponse> {
291   let form: ResolveObject = {
292     q: post.ap_id,
293     auth: api.auth,
294   };
295   return api.client.resolveObject(form);
296 }
297
298 export async function searchPostLocal(
299   api: API,
300   post: Post,
301 ): Promise<SearchResponse> {
302   let form: Search = {
303     q: post.name,
304     type_: "Posts",
305     sort: "TopAll",
306     auth: api.auth,
307   };
308   return api.client.search(form);
309 }
310
311 export async function getPost(
312   api: API,
313   post_id: number,
314 ): Promise<GetPostResponse> {
315   let form: GetPost = {
316     id: post_id,
317     auth: api.auth,
318   };
319   return api.client.getPost(form);
320 }
321
322 export async function getComments(
323   api: API,
324   post_id: number,
325 ): Promise<GetCommentsResponse> {
326   let form: GetComments = {
327     post_id: post_id,
328     type_: "All",
329     sort: "New",
330     auth: api.auth,
331   };
332   return api.client.getComments(form);
333 }
334
335 export async function getUnreadCount(
336   api: API,
337 ): Promise<GetUnreadCountResponse> {
338   let form: GetUnreadCount = {
339     auth: api.auth,
340   };
341   return api.client.getUnreadCount(form);
342 }
343
344 export async function getReplies(api: API): Promise<GetRepliesResponse> {
345   let form: GetReplies = {
346     sort: "New",
347     unread_only: false,
348     auth: api.auth,
349   };
350   return api.client.getReplies(form);
351 }
352
353 export async function resolveComment(
354   api: API,
355   comment: Comment,
356 ): Promise<ResolveObjectResponse> {
357   let form: ResolveObject = {
358     q: comment.ap_id,
359     auth: api.auth,
360   };
361   return api.client.resolveObject(form);
362 }
363
364 export async function resolveBetaCommunity(
365   api: API,
366 ): Promise<ResolveObjectResponse> {
367   // Use short-hand search url
368   let form: ResolveObject = {
369     q: "!main@lemmy-beta:8551",
370     auth: api.auth,
371   };
372   return api.client.resolveObject(form);
373 }
374
375 export async function resolveCommunity(
376   api: API,
377   q: string,
378 ): Promise<ResolveObjectResponse> {
379   let form: ResolveObject = {
380     q,
381     auth: api.auth,
382   };
383   return api.client.resolveObject(form);
384 }
385
386 export async function resolvePerson(
387   api: API,
388   apShortname: string,
389 ): Promise<ResolveObjectResponse> {
390   let form: ResolveObject = {
391     q: apShortname,
392     auth: api.auth,
393   };
394   return api.client.resolveObject(form);
395 }
396
397 export async function banPersonFromSite(
398   api: API,
399   person_id: number,
400   ban: boolean,
401   remove_data: boolean,
402 ): Promise<BanPersonResponse> {
403   // Make sure lemmy-beta/c/main is cached on lemmy_alpha
404   let form: BanPerson = {
405     person_id,
406     ban,
407     remove_data: remove_data,
408     auth: api.auth,
409   };
410   return api.client.banPerson(form);
411 }
412
413 export async function banPersonFromCommunity(
414   api: API,
415   person_id: number,
416   community_id: number,
417   remove_data: boolean,
418   ban: boolean,
419 ): Promise<BanFromCommunityResponse> {
420   let form: BanFromCommunity = {
421     person_id,
422     community_id,
423     remove_data: remove_data,
424     ban,
425     auth: api.auth,
426   };
427   return api.client.banFromCommunity(form);
428 }
429
430 export async function followCommunity(
431   api: API,
432   follow: boolean,
433   community_id: number,
434 ): Promise<CommunityResponse> {
435   let form: FollowCommunity = {
436     community_id,
437     follow,
438     auth: api.auth,
439   };
440   return api.client.followCommunity(form);
441 }
442
443 export async function likePost(
444   api: API,
445   score: number,
446   post: Post,
447 ): Promise<PostResponse> {
448   let form: CreatePostLike = {
449     post_id: post.id,
450     score: score,
451     auth: api.auth,
452   };
453
454   return api.client.likePost(form);
455 }
456
457 export async function createComment(
458   api: API,
459   post_id: number,
460   parent_id?: number,
461   content = "a jest test comment",
462 ): Promise<CommentResponse> {
463   let form: CreateComment = {
464     content,
465     post_id,
466     parent_id,
467     auth: api.auth,
468   };
469   return api.client.createComment(form);
470 }
471
472 export async function editComment(
473   api: API,
474   comment_id: number,
475   content = "A jest test federated comment update",
476 ): Promise<CommentResponse> {
477   let form: EditComment = {
478     content,
479     comment_id,
480     auth: api.auth,
481   };
482   return api.client.editComment(form);
483 }
484
485 export async function deleteComment(
486   api: API,
487   deleted: boolean,
488   comment_id: number,
489 ): Promise<CommentResponse> {
490   let form: DeleteComment = {
491     comment_id,
492     deleted,
493     auth: api.auth,
494   };
495   return api.client.deleteComment(form);
496 }
497
498 export async function removeComment(
499   api: API,
500   removed: boolean,
501   comment_id: number,
502 ): Promise<CommentResponse> {
503   let form: RemoveComment = {
504     comment_id,
505     removed,
506     auth: api.auth,
507   };
508   return api.client.removeComment(form);
509 }
510
511 export async function getMentions(
512   api: API,
513 ): Promise<GetPersonMentionsResponse> {
514   let form: GetPersonMentions = {
515     sort: "New",
516     unread_only: false,
517     auth: api.auth,
518   };
519   return api.client.getPersonMentions(form);
520 }
521
522 export async function likeComment(
523   api: API,
524   score: number,
525   comment: Comment,
526 ): Promise<CommentResponse> {
527   let form: CreateCommentLike = {
528     comment_id: comment.id,
529     score,
530     auth: api.auth,
531   };
532   return api.client.likeComment(form);
533 }
534
535 export async function createCommunity(
536   api: API,
537   name_: string = randomString(5),
538 ): Promise<CommunityResponse> {
539   let description = "a sample description";
540   let form: CreateCommunity = {
541     name: name_,
542     title: name_,
543     description,
544     auth: api.auth,
545   };
546   return api.client.createCommunity(form);
547 }
548
549 export async function getCommunity(
550   api: API,
551   id: number,
552 ): Promise<CommunityResponse> {
553   let form: GetCommunity = {
554     id,
555     auth: api.auth,
556   };
557   return api.client.getCommunity(form);
558 }
559
560 export async function deleteCommunity(
561   api: API,
562   deleted: boolean,
563   community_id: number,
564 ): Promise<CommunityResponse> {
565   let form: DeleteCommunity = {
566     community_id,
567     deleted,
568     auth: api.auth,
569   };
570   return api.client.deleteCommunity(form);
571 }
572
573 export async function removeCommunity(
574   api: API,
575   removed: boolean,
576   community_id: number,
577 ): Promise<CommunityResponse> {
578   let form: RemoveCommunity = {
579     community_id,
580     removed,
581     auth: api.auth,
582   };
583   return api.client.removeCommunity(form);
584 }
585
586 export async function createPrivateMessage(
587   api: API,
588   recipient_id: number,
589 ): Promise<PrivateMessageResponse> {
590   let content = "A jest test federated private message";
591   let form: CreatePrivateMessage = {
592     content,
593     recipient_id,
594     auth: api.auth,
595   };
596   return api.client.createPrivateMessage(form);
597 }
598
599 export async function editPrivateMessage(
600   api: API,
601   private_message_id: number,
602 ): Promise<PrivateMessageResponse> {
603   let updatedContent = "A jest test federated private message edited";
604   let form: EditPrivateMessage = {
605     content: updatedContent,
606     private_message_id,
607     auth: api.auth,
608   };
609   return api.client.editPrivateMessage(form);
610 }
611
612 export async function deletePrivateMessage(
613   api: API,
614   deleted: boolean,
615   private_message_id: number,
616 ): Promise<PrivateMessageResponse> {
617   let form: DeletePrivateMessage = {
618     deleted,
619     private_message_id,
620     auth: api.auth,
621   };
622   return api.client.deletePrivateMessage(form);
623 }
624
625 export async function registerUser(
626   api: API,
627   username: string = randomString(5),
628 ): Promise<LoginResponse> {
629   let form: Register = {
630     username,
631     password,
632     password_verify: password,
633     show_nsfw: true,
634   };
635   return api.client.register(form);
636 }
637
638 export async function saveUserSettingsBio(api: API): Promise<LoginResponse> {
639   let form: SaveUserSettings = {
640     show_nsfw: true,
641     blur_nsfw: false,
642     auto_expand: true,
643     theme: "darkly",
644     default_sort_type: "Active",
645     default_listing_type: "All",
646     interface_language: "en",
647     show_avatars: true,
648     send_notifications_to_email: false,
649     bio: "a changed bio",
650     auth: api.auth,
651   };
652   return saveUserSettings(api, form);
653 }
654
655 export async function saveUserSettingsFederated(
656   api: API,
657 ): Promise<LoginResponse> {
658   let avatar = "https://image.flaticon.com/icons/png/512/35/35896.png";
659   let banner = "https://image.flaticon.com/icons/png/512/36/35896.png";
660   let bio = "a changed bio";
661   let form: SaveUserSettings = {
662     show_nsfw: false,
663     blur_nsfw: true,
664     auto_expand: false,
665     default_sort_type: "Hot",
666     default_listing_type: "All",
667     interface_language: "",
668     avatar,
669     banner,
670     display_name: "user321",
671     show_avatars: false,
672     send_notifications_to_email: false,
673     bio,
674     auth: api.auth,
675   };
676   return await saveUserSettings(alpha, form);
677 }
678
679 export async function saveUserSettings(
680   api: API,
681   form: SaveUserSettings,
682 ): Promise<LoginResponse> {
683   return api.client.saveUserSettings(form);
684 }
685 export async function getPersonDetails(
686   api: API,
687   person_id: number,
688 ): Promise<GetPersonDetailsResponse> {
689   let form: GetPersonDetails = {
690     auth: api.auth,
691     person_id: person_id,
692   };
693   return api.client.getPersonDetails(form);
694 }
695
696 export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
697   let form: DeleteAccount = {
698     auth: api.auth,
699     password,
700   };
701   return api.client.deleteAccount(form);
702 }
703
704 export async function getSite(api: API): Promise<GetSiteResponse> {
705   let form: GetSite = {
706     auth: api.auth,
707   };
708   return api.client.getSite(form);
709 }
710
711 export async function listPrivateMessages(
712   api: API,
713 ): Promise<PrivateMessagesResponse> {
714   let form: GetPrivateMessages = {
715     auth: api.auth,
716     unread_only: false,
717   };
718   return api.client.getPrivateMessages(form);
719 }
720
721 export async function unfollowRemotes(api: API): Promise<GetSiteResponse> {
722   // Unfollow all remote communities
723   let site = await getSite(api);
724   let remoteFollowed =
725     site.my_user?.follows.filter(c => c.community.local == false) ?? [];
726   for (let cu of remoteFollowed) {
727     await followCommunity(api, false, cu.community.id);
728   }
729   let siteRes = await getSite(api);
730   return siteRes;
731 }
732
733 export async function followBeta(api: API): Promise<CommunityResponse> {
734   let betaCommunity = (await resolveBetaCommunity(api)).community;
735   if (betaCommunity) {
736     let follow = await followCommunity(api, true, betaCommunity.community.id);
737     return follow;
738   } else {
739     return Promise.reject("no community worked");
740   }
741 }
742
743 export async function reportPost(
744   api: API,
745   post_id: number,
746   reason: string,
747 ): Promise<PostReportResponse> {
748   let form: CreatePostReport = {
749     post_id,
750     reason,
751     auth: api.auth,
752   };
753   return api.client.createPostReport(form);
754 }
755
756 export async function listPostReports(
757   api: API,
758 ): Promise<ListPostReportsResponse> {
759   let form: ListPostReports = {
760     auth: api.auth,
761   };
762   return api.client.listPostReports(form);
763 }
764
765 export async function reportComment(
766   api: API,
767   comment_id: number,
768   reason: string,
769 ): Promise<CommentReportResponse> {
770   let form: CreateCommentReport = {
771     comment_id,
772     reason,
773     auth: api.auth,
774   };
775   return api.client.createCommentReport(form);
776 }
777
778 export async function listCommentReports(
779   api: API,
780 ): Promise<ListCommentReportsResponse> {
781   let form: ListCommentReports = {
782     auth: api.auth,
783   };
784   return api.client.listCommentReports(form);
785 }
786
787 export function getPosts(
788   api: API,
789   moderator_view = false,
790 ): Promise<GetPostsResponse> {
791   let form: GetPosts = {
792     moderator_view,
793     auth: api.auth,
794   };
795   return api.client.getPosts(form);
796 }
797
798 export function delay(millis = 500) {
799   return new Promise(resolve => setTimeout(resolve, millis));
800 }
801
802 export function longDelay() {
803   return delay(10000);
804 }
805
806 export function wrapper(form: any): string {
807   return JSON.stringify(form);
808 }
809
810 export function randomString(length: number): string {
811   var result = "";
812   var characters =
813     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
814   var charactersLength = characters.length;
815   for (var i = 0; i < length; i++) {
816     result += characters.charAt(Math.floor(Math.random() * charactersLength));
817   }
818   return result;
819 }
820
821 export async function unfollows() {
822   await unfollowRemotes(alpha);
823   await unfollowRemotes(gamma);
824   await unfollowRemotes(delta);
825   await unfollowRemotes(epsilon);
826 }
827
828 export function getCommentParentId(comment: Comment): number | undefined {
829   let split = comment.path.split(".");
830   // remove the 0
831   split.shift();
832
833   if (split.length > 1) {
834     return Number(split[split.length - 2]);
835   } else {
836     return undefined;
837   }
838 }