announcements.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { Api, Member, ProposalDetail, Proposals } from "../types";
  2. import { BlockNumber } from "@polkadot/types/interfaces";
  3. import { Channel, ElectionStage } from "@joystream/types/augment";
  4. import { Category, Thread, Post } from "@joystream/types/forum";
  5. import { domain } from "../../config";
  6. import {
  7. categoryById,
  8. memberHandle,
  9. memberHandleByAccount,
  10. proposalDetail
  11. } from "./getters";
  12. const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
  13. const query = async (test: string, cb: () => Promise<any>): Promise<any> => {
  14. let result = await cb();
  15. for (let i: number = 0; i < 10; i++) {
  16. if (result[test] !== "") return result;
  17. result = await cb();
  18. await sleep(5000);
  19. }
  20. };
  21. export const channels = async (
  22. api: Api,
  23. channels: number[],
  24. sendMessage: (msg: string) => void
  25. ): Promise<number> => {
  26. const [last, current] = channels;
  27. const messages: string[] = [];
  28. for (let id: number = last + 1; id <= current; id++) {
  29. const channel: Channel = await query("title", () =>
  30. api.query.contentWorkingGroup.channelById(id)
  31. );
  32. const member: Member = { id: channel.owner, handle: "", url: "" };
  33. member.handle = await memberHandle(api, member.id.toJSON());
  34. member.url = `${domain}/#/members/${member.handle}`;
  35. messages.push(
  36. `<b>Channel <a href="${domain}/#//media/channels/${id}">${channel.title}</a> by <a href="${member.url}">${member.handle} (${member.id})</a></b>`
  37. );
  38. }
  39. sendMessage(messages.join("\r\n\r\n"));
  40. return current;
  41. };
  42. export const councils = async (
  43. api: Api,
  44. currentBlock: number,
  45. sendMessage: (msg: string) => void
  46. ): Promise<number> => {
  47. let lastBlock: number = currentBlock;
  48. const round: number = await api.query.councilElection.round();
  49. const stage: ElectionStage | null = await api.query.councilElection.stage();
  50. let msg = "";
  51. if (!stage) {
  52. const councilEnd: BlockNumber = await api.query.council.termEndsAt();
  53. lastBlock = councilEnd.toNumber();
  54. const termDuration: BlockNumber = await api.query.councilElection.newTermDuration();
  55. const block = lastBlock - termDuration.toNumber();
  56. msg = `<a href="${domain}/#/council/members">Council for round ${round}</a> has been elected at block ${block} until block ${councilEnd}.`;
  57. } else {
  58. if (stage.isAnnouncing) {
  59. lastBlock = stage.asAnnouncing.toNumber();
  60. const announcingPeriod: BlockNumber = await api.query.councilElection.announcingPeriod();
  61. const block = lastBlock - announcingPeriod.toNumber();
  62. msg = `Announcing election for round ${round} at ${block}.<a href="${domain}/#/council/applicants">Apply now!</a>`;
  63. } else if (stage.isVoting) {
  64. lastBlock = stage.asVoting.toNumber();
  65. const votingPeriod: BlockNumber = await api.query.councilElection.votingPeriod();
  66. const block = lastBlock - votingPeriod.toNumber();
  67. msg = `Voting stage for council election started at block ${block}. <a href="${domain}/#/council/applicants">Vote now!</a>`;
  68. } else if (stage.isRevealing) {
  69. lastBlock = stage.asRevealing.toNumber();
  70. const revealingPeriod: BlockNumber = await api.query.councilElection.revealingPeriod();
  71. const block = lastBlock - revealingPeriod.toNumber();
  72. msg = `Revealing stage for council election started at block ${block}. <a href="${domain}/#/council/votes">Don't forget to reveal your vote!</a>`;
  73. }
  74. }
  75. sendMessage(msg);
  76. return lastBlock;
  77. };
  78. // forum
  79. export const categories = async (
  80. api: Api,
  81. category: number[],
  82. sendMessage: (msg: string) => void
  83. ): Promise<number> => {
  84. const messages: string[] = [];
  85. let id: number = category[0] + 1;
  86. for (id; id <= category[1]; id++) {
  87. const cat: Category = await query("title", () => categoryById(api, id));
  88. const msg = `Category ${id}: <b><a href="${domain}/#/forum/categories/${id}">${cat.title}</a></b>`;
  89. messages.push(msg);
  90. }
  91. sendMessage(messages.join("\r\n\r\n"));
  92. return category[1];
  93. };
  94. export const posts = async (
  95. api: Api,
  96. posts: number[],
  97. sendMessage: (msg: string) => void
  98. ): Promise<number> => {
  99. const [last, current] = posts;
  100. const messages: string[] = [];
  101. let id: number = last + 1;
  102. for (id; id <= current; id++) {
  103. const post: Post = await query("current_text", () =>
  104. api.query.forum.postById(id)
  105. );
  106. const replyId: number = post.nr_in_thread.toNumber();
  107. const message: string = post.current_text;
  108. const excerpt: string = message.substring(0, 100);
  109. const threadId: number = post.thread_id.toNumber();
  110. const thread: Thread = await query("title", () =>
  111. api.query.forum.threadById(threadId)
  112. );
  113. const threadTitle: string = thread.title;
  114. const category: Category = await query("title", () =>
  115. categoryById(api, thread.category_id.toNumber())
  116. );
  117. const handle = await memberHandleByAccount(api, post.author_id.toJSON());
  118. const msg = `<b><a href="${domain}/#/members/${handle}">${handle}</a> posted <a href="${domain}/#/forum/threads/${threadId}?replyIdx=${replyId}">${threadTitle}</a> in <a href="${domain}/#/forum/categories/${category.id}">${category.title}</a>:</b>\n\r<i>${excerpt}</i> <a href="${domain}/#/forum/threads/${threadId}?replyIdx=${replyId}">more</a>`;
  119. messages.push(msg);
  120. }
  121. sendMessage(messages.join("\r\n\r\n"));
  122. return current;
  123. };
  124. export const threads = async (
  125. api: Api,
  126. threads: number[],
  127. sendMessage: (msg: string) => void
  128. ): Promise<number> => {
  129. const [last, current] = threads;
  130. const messages: string[] = [];
  131. let id: number = last + 1;
  132. for (id; id <= current; id++) {
  133. const thread: Thread = await query("title", () =>
  134. api.query.forum.threadById(id)
  135. );
  136. const { title, author_id } = thread;
  137. const handle: string = await memberHandleByAccount(api, author_id.toJSON());
  138. const category: Category = await query("title", () =>
  139. categoryById(api, thread.category_id.toNumber())
  140. );
  141. const msg = `Thread ${id}: <a href="${domain}/#/forum/threads/${id}">"${title}"</a> by <a href="${domain}/#/members/${handle}">${handle}</a> in category "<a href="${domain}/#/forum/categories/${category.id}">${category.title}</a>" `;
  142. messages.push(msg);
  143. }
  144. sendMessage(messages.join("\r\n\r\n"));
  145. return id;
  146. };
  147. // proposals
  148. const processActive = async (
  149. id: number,
  150. details: ProposalDetail,
  151. sendMessage: (s: string) => void
  152. ): Promise<boolean> => {
  153. const { createdAt, finalizedAt, message, parameters, result } = details;
  154. let msg = `Proposal ${id} <b>created</b> at block ${createdAt}.\r\n${message}`;
  155. if (details.stage === "Finalized") {
  156. let label: string = result;
  157. if (result === "Approved") {
  158. const executed = parameters.gracePeriod.toNumber() > 0 ? false : true;
  159. label = executed ? "Finalized" : "Executed";
  160. }
  161. msg = `Proposal ${id} <b>${label}</b> at block ${finalizedAt}.\r\n${message}`;
  162. sendMessage(msg);
  163. return true;
  164. } else return processPending(id, details, sendMessage);
  165. };
  166. const processPending = async (
  167. id: number,
  168. details: ProposalDetail,
  169. sendMessage: (s: string) => void
  170. ): Promise<boolean> => {
  171. const { createdAt, message, parameters, stage } = details;
  172. if (stage === "Finalized") return processActive(id, details, sendMessage);
  173. const votingEndsAt = createdAt + parameters.votingPeriod.toNumber();
  174. const msg = `Proposal ${id} <b>created</b> at block ${createdAt}.\r\n${message}\r\nYou can vote until block ${votingEndsAt}.`;
  175. sendMessage(msg);
  176. return true;
  177. };
  178. export const proposals = async (
  179. api: Api,
  180. prop: Proposals,
  181. sendMessage: (msg: string) => void
  182. ): Promise<Proposals> => {
  183. let { current, last, active, pending } = prop;
  184. for (let id: number = last++; id <= current; id++) active.push(id);
  185. for (const id of active)
  186. if (processActive(id, await proposalDetail(api, id), sendMessage))
  187. active = active.filter((e: number) => e !== id);
  188. for (const id of pending)
  189. if (processPending(id, await proposalDetail(api, id), sendMessage))
  190. pending = pending.filter((e: number) => e !== id);
  191. return { current, last: current, active, pending };
  192. };
  193. export const formatProposalMessage = (data: string[]): string => {
  194. const [id, title, type, stage, result, handle] = data;
  195. return `<b>Type</b>: ${type}\r\n<b>Proposer</b>:<a href="${domain}/#/members/${handle}"> ${handle}</a>\r\n<b>Title</b>: <a href="${domain}/#/proposals/${id}">${title}</a>\r\n<b>Stage</b>: ${stage}\r\n<b>Result</b>: ${result}`;
  196. };