app.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import moment from "moment";
  2. import _debug from "debug";
  3. import {Markup, CallbackButton} from "telegraf";
  4. const debug = _debug("totify:bot/app");
  5. import cmdRegistry from "../commandRegistry";
  6. import telegraf from "../telegraf";
  7. import db from "../db";
  8. import _ie from "../utils/internalError";
  9. const ie = _ie("bot/app");
  10. import parseId from "../utils/parseId";
  11. import { Context } from "./auth";
  12. /**
  13. * Fetch 5 not activated apps and return buttons for user to activate or remove them
  14. *
  15. * @returns {Promise<CallbackButton[][]>} - Telegraf buttons
  16. */
  17. async function fetchPendingApps(): Promise<CallbackButton[][]> {
  18. let pendingApps = await db.App.findAll({
  19. where: {
  20. activated: false
  21. },
  22. limit: 5
  23. });
  24. let apps = pendingApps.map(el => {
  25. return [
  26. Markup.callbackButton(`✔️[${el.id}] ${el.name}`, `activate ${el.id}`),
  27. Markup.callbackButton(`❌[${el.id}] ${el.name}`, `deactivate ${el.id}`)
  28. ]
  29. });
  30. return apps;
  31. }
  32. /**
  33. * Get pending apps and wrap button with inlineKeyboard
  34. *
  35. * @param {*} [apps]
  36. * @returns {Promise<object>} - Telegraf keyboard
  37. */
  38. async function getPAppsKeyboard(apps?: any): Promise<object> {
  39. if (!apps) {
  40. apps = await fetchPendingApps();
  41. }
  42. return Markup.inlineKeyboard(apps)
  43. .resize()
  44. .extra();
  45. }
  46. /**
  47. * Register app related commands
  48. *
  49. */
  50. function app() {
  51. if (telegraf.bot == null) {
  52. throw new Error("Bot uninitialized");
  53. }
  54. telegraf.bot.action(/^activate (\d+)$/, async (ctx) => {
  55. try {
  56. if(!ctx.match){
  57. throw new Error("There is no match attribute");
  58. }
  59. debug("Activating app", ctx.match[1]);
  60. let id = ctx.match[1];
  61. let app = await db.App.findByPk(id);
  62. if(app == null){
  63. throw new Error(`There is no app with id equeal ${id}`);
  64. }
  65. await app.update({
  66. activated: true
  67. })
  68. ctx.editMessageText(`[${app.name}] Activated`, await getPAppsKeyboard());
  69. } catch (e) {
  70. ie(1, e);
  71. }
  72. });
  73. telegraf.bot.action(/^deactivate (\d+)$/, async (ctx) => {
  74. try {
  75. if(!ctx.match){
  76. throw new Error("There is no match attribute");
  77. }
  78. debug("Deactivating app", ctx.match[1]);
  79. let id = ctx.match[1];
  80. let app = await db.App.findByPk(id);
  81. if(app == null){
  82. throw new Error(`There is no app with id equeal ${id}`);
  83. }
  84. await app.destroy();
  85. await ctx.editMessageText(`[${app.name}] Removed`, await getPAppsKeyboard());
  86. } catch (e) {
  87. ie(2, e);
  88. }
  89. });
  90. // List not activated apps
  91. cmdRegistry.register("pendingApps", async (ctx) => {
  92. try {
  93. let pendingApps = await fetchPendingApps();
  94. if (pendingApps.length <= 0) {
  95. ctx.reply("No application is waiting for activation");
  96. return;
  97. }
  98. return ctx.reply('Apps to activate', await getPAppsKeyboard(pendingApps));
  99. } catch (e) {
  100. ie(3, e);
  101. }
  102. });
  103. // Register an App with name passed by argument
  104. // Return Name and authcode of new app instance
  105. cmdRegistry.register("registerApp", async (ctx: Context) => {
  106. try {
  107. if(!ctx.state){
  108. throw new Error("There is no state attribute");
  109. }
  110. const args: string = await ctx.state.command.args;
  111. if (args.length > 25) {
  112. ctx.reply("Name to long!");
  113. return;
  114. }
  115. const appName = args;
  116. const instance = await db.App.create({
  117. name: appName,
  118. activated: true
  119. });
  120. ctx.reply(`App registered\n` +
  121. `Name: ${instance.name}\n` +
  122. `Auth: ${instance.auth}`);
  123. return;
  124. } catch (e) {
  125. ie(4, e);
  126. }
  127. });
  128. /**
  129. * Return list of apps paginated
  130. *
  131. * @param {number} page
  132. * @param {Context} ctx
  133. * @returns {Promise<[string, object]>}
  134. */
  135. async function printAppsList(page: number, ctx: Context): Promise<[string, object]> {
  136. let apps = await db.App.findAndCountAll({
  137. limit: 5,
  138. offset: 5 * page
  139. });
  140. const count = apps.count;
  141. const maxPage = Math.floor(count / 5);
  142. if (apps.rows.length <= 0) {
  143. ctx.reply("No apps to show");
  144. throw new Error("No apps to show");
  145. }
  146. let msg = apps.rows.map(el => {
  147. return {
  148. i: el.id,
  149. n: el.name,
  150. a: (el.activated ? "Activated" : "Not activated")
  151. }
  152. }).map(el => `[${el.i}](${el.n})\t${el.a}\n`).join("");
  153. const keyboard = [];
  154. if (page > 0) {
  155. keyboard.push(Markup.callbackButton(`<< Before`, `apps ${page - 1}`));
  156. }
  157. if (page < maxPage) {
  158. keyboard.push(Markup.callbackButton(`Next >>`, `apps ${page + 1}`));
  159. }
  160. return [msg, Markup.inlineKeyboard(keyboard).extra()];
  161. }
  162. // Return list of apps
  163. cmdRegistry.register("apps", async (ctx: Context) => {
  164. try {
  165. if(!ctx.state){
  166. throw new Error("There is no state attribute");
  167. }
  168. const page = parseId(ctx.state.command.splitArgs[0]) - 1;
  169. let args = await printAppsList(page, ctx);
  170. return ctx.reply.apply(ctx, args);
  171. } catch (e) {
  172. ie(5, e);
  173. }
  174. });
  175. // Pagination support for list
  176. telegraf.bot.action(/^apps (\d+)$/, async (ctx) => {
  177. try {
  178. if(!ctx.match){
  179. throw new Error("There is no match attribute");
  180. }
  181. const page = parseInt(ctx.match[1]);
  182. let args = await printAppsList(page, ctx);
  183. if(args == undefined){
  184. throw new Error("There is no app on list");
  185. }
  186. return ctx.editMessageText.apply(ctx, args);
  187. } catch (e) {
  188. ie(6, e);
  189. }
  190. });
  191. }
  192. export default app;