Baphomet is the dedicated bot for nulloctet matrix
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
7.7 KiB

  1. let fs = require('fs');
  2. let sdk = require('matrix-js-sdk');
  3. let message = require('./message');
  4. let utility = require('./utility');
  5. let { logger } = require('./logging');
  6. class Bot {
  7. client: any;
  8. config: any;
  9. buildInfo: string;
  10. connected: boolean;
  11. startTime: Date;
  12. constructor(config, buildInfo) {
  13. this.config = config;
  14. this.buildInfo = buildInfo;
  15. this.connected = false;
  16. this.startTime = new Date();
  17. }
  18. /**
  19. * Initialize the bot connection
  20. */
  21. init(messageCallback: any) {
  22. logger.info("Creating Matrix Client")
  23. this.client = sdk.createClient({
  24. baseUrl: this.config.baseUrl,
  25. userId: this.config.userId
  26. });
  27. this.client.on("sync", async (state: any, previousState: any, data: any) => {
  28. switch (state) {
  29. case "PREPARED":
  30. this.connected = true;
  31. await this.sendStatusStartup();
  32. await this.updateAvatar(process.env.NODE_PATH + '/assets/avatar.jpg');
  33. this.client.getJoinedRooms()
  34. .done((rooms) => {
  35. logger.info("Connected to: %o", rooms)
  36. });
  37. break;
  38. case "SYNCING":
  39. logger.debug("Syncing")
  40. break;
  41. case "RECONNECTING":
  42. logger.debug("Reconnecting");
  43. break;
  44. default:
  45. logger.error("Unexpected sync state: %s", state);
  46. process.exit(1);
  47. }
  48. });
  49. this.client.on("RoomMember.membership", (event: any, member: any) => {
  50. if (member.membership === "invite"
  51. && this.config.admin.indexOf(member.userId) >= 0) {
  52. this.client.joinRoom(member.roomId).done(() => {
  53. logger.info("Auto-joined %s", member.roomId);
  54. });
  55. }
  56. });
  57. this.client.on("Room.timeline", messageCallback);
  58. return this;
  59. }
  60. async connect() {
  61. // logger.info("Initializing Crypto");
  62. // await bot.client.initCrypto();
  63. let botClient = this.client;
  64. let botConfig = this.config;
  65. let startServerConnection = () => {
  66. logger.info("Starting Matrix SDK Client");
  67. return this.client.startClient({
  68. initialSyncLimit: 0
  69. });
  70. }
  71. let attemptPasswordLogin = (botClient, botConfig) => {
  72. return botClient.loginWithPassword(botConfig.userId, botConfig.userPassword)
  73. .then((data) => {
  74. logger.info("Successfully authenticated with password %o", data);
  75. return startServerConnection();
  76. }, (err) => {
  77. logger.error("Failed to authenticate with password %o", err);
  78. });
  79. }
  80. logger.info("Authenticating With Server");
  81. if (typeof botConfig.accessToken !== 'undefined') {
  82. await botClient.loginWithToken(this.config.accessToken)
  83. .then((data) => {
  84. logger.info("Successfully authenticated with access token %o", data);
  85. return startServerConnection();
  86. }, (err) => {
  87. logger.error("Failed to authenticate with access token %o", err);
  88. if (typeof botConfig.userPassword !== 'undefined') {
  89. return attemptPasswordLogin(botClient, botConfig);
  90. } else {
  91. logger.error("No fallback password provided!");
  92. }
  93. });
  94. } else if (typeof botConfig.userPassword !== 'undefined') {
  95. await attemptPasswordLogin(botClient, botConfig);
  96. } else {
  97. logger.error("No authentication credentials available!");
  98. process.exit(1);
  99. }
  100. }
  101. updateAvatar(avatarFile, overwrite = false) {
  102. let matrixClient = this.client;
  103. let botConfig = this.config;
  104. let promises = [Promise.resolve(true)];
  105. if (this.connected) {
  106. promises.push(this.client.getProfileInfo(this.config.userId, "avatar_url")
  107. .then((existingAvatarUrl) => {
  108. logger.info("Recieved avatar_url: %o", existingAvatarUrl);
  109. if (typeof existingAvatarUrl !== 'undefined' && typeof existingAvatarUrl == 'object'
  110. && existingAvatarUrl.constructor === Object && Object.keys(existingAvatarUrl).length !== 0
  111. && !overwrite) {
  112. logger.info("Avatar already set");
  113. } else {
  114. logger.info("Setting avatar content from %s", avatarFile);
  115. let avatarFileBuffer = fs.readFileSync(avatarFile);
  116. logger.debug("Avatar Image Data %o", avatarFileBuffer);
  117. matrixClient.uploadContent(avatarFileBuffer, {
  118. name: botConfig.userId + " avatar",
  119. type: "image/jpeg",
  120. rawResponse: false
  121. }).then((uploadedAvatar) => {
  122. logger.info("Uploaded avatar %o", uploadedAvatar);
  123. matrixClient.setAvatarUrl(uploadedAvatar.content_uri)
  124. .then(() => {
  125. logger.info("Updated %s avatar to %s", botConfig.userId, uploadedAvatar.content_uri);
  126. return true;
  127. });
  128. });
  129. }
  130. }));
  131. } else {
  132. logger.warn("Attempting to update avatar while disconnected");
  133. }
  134. return Promise.all(promises);
  135. }
  136. sendStatusStartup() {
  137. let promises = [Promise.resolve(true)]
  138. if (this.connected) {
  139. this.config.statusRooms.forEach(roomId => {
  140. logger.debug("Notifying %s of startup", roomId);
  141. promises.push(this.client.sendMessage(
  142. roomId, message.createBasic(`Started at ${utility.toISODateString(this.startTime)} with version: ${this.buildInfo}`, message.types.NOTICE)
  143. ).then(() => {
  144. logger.debug("Notified %s of startup", roomId);
  145. }, (err) => {
  146. logger.error("Unable to send message to room %s because %s", roomId, err.errcode);
  147. }));
  148. });
  149. } else {
  150. logger.warn("Attempting to send startup message while disconnected");
  151. }
  152. return Promise.all(promises);
  153. }
  154. sendStatusShutdown() {
  155. let promises = [Promise.resolve(true)]
  156. if (this.connected) {
  157. this.config.statusRooms.forEach(roomId => {
  158. logger.debug("Notifying %s of shutdown", roomId);
  159. promises.push(this.client.sendMessage(
  160. roomId, message.createBasic(`Shutting down at ${utility.toISODateString(new Date())} with version: ${this.buildInfo}`, message.types.NOTICE)
  161. ).then(() => {
  162. logger.debug("Notified %s of shutdown", roomId);
  163. }, (err) => {
  164. logger.error("Unable to send message to room %s because %s", roomId, err.errcode);
  165. }));
  166. });
  167. } else {
  168. logger.warn("Attempting to send shutdown message while disconnected");
  169. }
  170. return Promise.all(promises);
  171. }
  172. }
  173. function create(config) {
  174. let buildInfo = utility.getBuildInfo();
  175. logger.info("Running version: %s", buildInfo);
  176. return new Bot(config, buildInfo);
  177. }
  178. export { create };