agent-messenger 2.29.1 → 2.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +15 -0
  3. package/bun.lock +42 -2
  4. package/dist/package.json +2 -2
  5. package/dist/src/cli.d.ts.map +1 -1
  6. package/dist/src/cli.js.map +1 -1
  7. package/dist/src/platforms/channeltalk/cli.d.ts.map +1 -1
  8. package/dist/src/platforms/channeltalk/client.d.ts.map +1 -1
  9. package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
  10. package/dist/src/platforms/channeltalk/commands/bot.js.map +1 -1
  11. package/dist/src/platforms/channeltalk/commands/chat.js.map +1 -1
  12. package/dist/src/platforms/channeltalk/commands/group.js.map +1 -1
  13. package/dist/src/platforms/channeltalk/commands/manager.js.map +1 -1
  14. package/dist/src/platforms/channeltalk/commands/message.js.map +1 -1
  15. package/dist/src/platforms/channeltalk/commands/shared.js.map +1 -1
  16. package/dist/src/platforms/channeltalk/commands/snapshot.js.map +1 -1
  17. package/dist/src/platforms/channeltalk/commands/whoami.js.map +1 -1
  18. package/dist/src/platforms/channeltalk/credential-manager.d.ts.map +1 -1
  19. package/dist/src/platforms/channeltalk/ensure-auth.d.ts.map +1 -1
  20. package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
  21. package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
  22. package/dist/src/platforms/channeltalk/types.d.ts.map +1 -1
  23. package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
  24. package/dist/src/platforms/channeltalkbot/client.d.ts.map +1 -1
  25. package/dist/src/platforms/channeltalkbot/client.js.map +1 -1
  26. package/dist/src/platforms/channeltalkbot/credential-manager.d.ts.map +1 -1
  27. package/dist/src/platforms/channeltalkbot/types.d.ts +1 -1
  28. package/dist/src/platforms/channeltalkbot/types.d.ts.map +1 -1
  29. package/dist/src/platforms/discord/cli.d.ts.map +1 -1
  30. package/dist/src/platforms/discord/client.d.ts +1 -0
  31. package/dist/src/platforms/discord/client.d.ts.map +1 -1
  32. package/dist/src/platforms/discord/client.js +39 -17
  33. package/dist/src/platforms/discord/client.js.map +1 -1
  34. package/dist/src/platforms/discord/commands/message.d.ts +3 -0
  35. package/dist/src/platforms/discord/commands/message.d.ts.map +1 -1
  36. package/dist/src/platforms/discord/commands/message.js +30 -0
  37. package/dist/src/platforms/discord/commands/message.js.map +1 -1
  38. package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -1
  39. package/dist/src/platforms/discord/listener.d.ts.map +1 -1
  40. package/dist/src/platforms/discord/listener.js.map +1 -1
  41. package/dist/src/platforms/discord/remote-auth.js.map +1 -1
  42. package/dist/src/platforms/discord/super-properties.js.map +1 -1
  43. package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
  44. package/dist/src/platforms/discord/token-extractor.js.map +1 -1
  45. package/dist/src/platforms/discord/types.d.ts +13 -0
  46. package/dist/src/platforms/discord/types.d.ts.map +1 -1
  47. package/dist/src/platforms/discord/types.js +6 -0
  48. package/dist/src/platforms/discord/types.js.map +1 -1
  49. package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
  50. package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
  51. package/dist/src/platforms/discordbot/client.js.map +1 -1
  52. package/dist/src/platforms/discordbot/commands/message.d.ts +1 -1
  53. package/dist/src/platforms/discordbot/commands/message.d.ts.map +1 -1
  54. package/dist/src/platforms/discordbot/commands/message.js +4 -4
  55. package/dist/src/platforms/discordbot/commands/message.js.map +1 -1
  56. package/dist/src/platforms/discordbot/credential-manager.d.ts.map +1 -1
  57. package/dist/src/platforms/discordbot/listener.d.ts.map +1 -1
  58. package/dist/src/platforms/discordbot/listener.js.map +1 -1
  59. package/dist/src/platforms/discordbot/types.d.ts.map +1 -1
  60. package/dist/src/platforms/imessage/cli.d.ts.map +1 -1
  61. package/dist/src/platforms/imessage/client.d.ts.map +1 -1
  62. package/dist/src/platforms/imessage/client.js.map +1 -1
  63. package/dist/src/platforms/imessage/credential-manager.d.ts.map +1 -1
  64. package/dist/src/platforms/imessage/errors.js.map +1 -1
  65. package/dist/src/platforms/imessage/rpc.d.ts.map +1 -1
  66. package/dist/src/platforms/imessage/rpc.js.map +1 -1
  67. package/dist/src/platforms/imessage/types.d.ts.map +1 -1
  68. package/dist/src/platforms/imessage/types.js.map +1 -1
  69. package/dist/src/platforms/instagram/cli.d.ts.map +1 -1
  70. package/dist/src/platforms/instagram/client.d.ts.map +1 -1
  71. package/dist/src/platforms/instagram/client.js.map +1 -1
  72. package/dist/src/platforms/instagram/credential-manager.d.ts.map +1 -1
  73. package/dist/src/platforms/instagram/hybrid-listener.d.ts.map +1 -1
  74. package/dist/src/platforms/instagram/hybrid-listener.js.map +1 -1
  75. package/dist/src/platforms/instagram/listener.d.ts.map +1 -1
  76. package/dist/src/platforms/instagram/mqtt/thrift.d.ts.map +1 -1
  77. package/dist/src/platforms/instagram/mqtt/transport.d.ts.map +1 -1
  78. package/dist/src/platforms/instagram/mqtt/transport.js.map +1 -1
  79. package/dist/src/platforms/instagram/realtime-listener.d.ts.map +1 -1
  80. package/dist/src/platforms/instagram/realtime-listener.js.map +1 -1
  81. package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -1
  82. package/dist/src/platforms/instagram/types.d.ts.map +1 -1
  83. package/dist/src/platforms/instagram/types.js.map +1 -1
  84. package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
  85. package/dist/src/platforms/kakaotalk/client.d.ts +5 -1
  86. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  87. package/dist/src/platforms/kakaotalk/client.js +55 -0
  88. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  89. package/dist/src/platforms/kakaotalk/commands/chat.d.ts.map +1 -1
  90. package/dist/src/platforms/kakaotalk/commands/chat.js +16 -1
  91. package/dist/src/platforms/kakaotalk/commands/chat.js.map +1 -1
  92. package/dist/src/platforms/kakaotalk/credential-manager.d.ts.map +1 -1
  93. package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
  94. package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
  95. package/dist/src/platforms/kakaotalk/index.js +1 -1
  96. package/dist/src/platforms/kakaotalk/index.js.map +1 -1
  97. package/dist/src/platforms/kakaotalk/listener.d.ts.map +1 -1
  98. package/dist/src/platforms/kakaotalk/listener.js.map +1 -1
  99. package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
  100. package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
  101. package/dist/src/platforms/kakaotalk/protocol/crypto.d.ts.map +1 -1
  102. package/dist/src/platforms/kakaotalk/protocol/session.d.ts +23 -0
  103. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  104. package/dist/src/platforms/kakaotalk/protocol/session.js +50 -0
  105. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  106. package/dist/src/platforms/kakaotalk/token-extractor.d.ts.map +1 -1
  107. package/dist/src/platforms/kakaotalk/types.d.ts +30 -10
  108. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  109. package/dist/src/platforms/kakaotalk/types.js +10 -0
  110. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  111. package/dist/src/platforms/line/cli.d.ts.map +1 -1
  112. package/dist/src/platforms/line/client.d.ts.map +1 -1
  113. package/dist/src/platforms/line/credential-manager.d.ts.map +1 -1
  114. package/dist/src/platforms/line/listener.d.ts.map +1 -1
  115. package/dist/src/platforms/line/types.d.ts +6 -6
  116. package/dist/src/platforms/line/types.d.ts.map +1 -1
  117. package/dist/src/platforms/slack/cli.d.ts.map +1 -1
  118. package/dist/src/platforms/slack/client.d.ts.map +1 -1
  119. package/dist/src/platforms/slack/client.js.map +1 -1
  120. package/dist/src/platforms/slack/commands/message.js +4 -4
  121. package/dist/src/platforms/slack/commands/message.js.map +1 -1
  122. package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -1
  123. package/dist/src/platforms/slack/ensure-auth.js.map +1 -1
  124. package/dist/src/platforms/slack/listener.d.ts.map +1 -1
  125. package/dist/src/platforms/slack/qr-http-login.js.map +1 -1
  126. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  127. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  128. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
  129. package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
  130. package/dist/src/platforms/slackbot/commands/message.js +4 -4
  131. package/dist/src/platforms/slackbot/commands/message.js.map +1 -1
  132. package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -1
  133. package/dist/src/platforms/slackbot/listener.d.ts.map +1 -1
  134. package/dist/src/platforms/slackbot/listener.js.map +1 -1
  135. package/dist/src/platforms/slackbot/types.d.ts.map +1 -1
  136. package/dist/src/platforms/teams/app-config.js.map +1 -1
  137. package/dist/src/platforms/teams/cli.d.ts.map +1 -1
  138. package/dist/src/platforms/teams/client.d.ts +1 -0
  139. package/dist/src/platforms/teams/client.d.ts.map +1 -1
  140. package/dist/src/platforms/teams/client.js +19 -0
  141. package/dist/src/platforms/teams/client.js.map +1 -1
  142. package/dist/src/platforms/teams/commands/chat.d.ts +3 -0
  143. package/dist/src/platforms/teams/commands/chat.d.ts.map +1 -1
  144. package/dist/src/platforms/teams/commands/chat.js +34 -1
  145. package/dist/src/platforms/teams/commands/chat.js.map +1 -1
  146. package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -1
  147. package/dist/src/platforms/teams/device-code.js.map +1 -1
  148. package/dist/src/platforms/teams/device-login.d.ts.map +1 -1
  149. package/dist/src/platforms/teams/device-login.js.map +1 -1
  150. package/dist/src/platforms/teams/listener.d.ts.map +1 -1
  151. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  152. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  153. package/dist/src/platforms/teams/token-provider.d.ts.map +1 -1
  154. package/dist/src/platforms/teams/token-provider.js.map +1 -1
  155. package/dist/src/platforms/teams/types.d.ts +14 -14
  156. package/dist/src/platforms/teams/types.d.ts.map +1 -1
  157. package/dist/src/platforms/telegram/cli.d.ts.map +1 -1
  158. package/dist/src/platforms/telegram/client.d.ts +1 -0
  159. package/dist/src/platforms/telegram/client.d.ts.map +1 -1
  160. package/dist/src/platforms/telegram/client.js +21 -0
  161. package/dist/src/platforms/telegram/client.js.map +1 -1
  162. package/dist/src/platforms/telegram/commands/message.d.ts.map +1 -1
  163. package/dist/src/platforms/telegram/commands/message.js +25 -1
  164. package/dist/src/platforms/telegram/commands/message.js.map +1 -1
  165. package/dist/src/platforms/telegram/commands/shared.js.map +1 -1
  166. package/dist/src/platforms/telegram/credential-manager.d.ts.map +1 -1
  167. package/dist/src/platforms/telegram/tdlib.d.ts.map +1 -1
  168. package/dist/src/platforms/telegram/types.d.ts.map +1 -1
  169. package/dist/src/platforms/telegram/types.js.map +1 -1
  170. package/dist/src/platforms/telegrambot/cli.d.ts.map +1 -1
  171. package/dist/src/platforms/telegrambot/client.d.ts.map +1 -1
  172. package/dist/src/platforms/telegrambot/commands/message.d.ts +1 -1
  173. package/dist/src/platforms/telegrambot/commands/message.d.ts.map +1 -1
  174. package/dist/src/platforms/telegrambot/commands/message.js +3 -3
  175. package/dist/src/platforms/telegrambot/commands/message.js.map +1 -1
  176. package/dist/src/platforms/telegrambot/credential-manager.d.ts.map +1 -1
  177. package/dist/src/platforms/telegrambot/listener.d.ts.map +1 -1
  178. package/dist/src/platforms/telegrambot/types.d.ts.map +1 -1
  179. package/dist/src/platforms/webex/cli.d.ts.map +1 -1
  180. package/dist/src/platforms/webex/client.d.ts.map +1 -1
  181. package/dist/src/platforms/webex/client.js.map +1 -1
  182. package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -1
  183. package/dist/src/platforms/webex/encryption.d.ts.map +1 -1
  184. package/dist/src/platforms/webex/kms-key-provider.d.ts.map +1 -1
  185. package/dist/src/platforms/webex/listener.d.ts.map +1 -1
  186. package/dist/src/platforms/webex/listener.js.map +1 -1
  187. package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
  188. package/dist/src/platforms/webex/types.d.ts +4 -4
  189. package/dist/src/platforms/webex/types.d.ts.map +1 -1
  190. package/dist/src/platforms/webexbot/cli.d.ts.map +1 -1
  191. package/dist/src/platforms/webexbot/client.d.ts.map +1 -1
  192. package/dist/src/platforms/webexbot/credential-manager.d.ts.map +1 -1
  193. package/dist/src/platforms/webexbot/listener.d.ts.map +1 -1
  194. package/dist/src/platforms/webexbot/types.d.ts.map +1 -1
  195. package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -1
  196. package/dist/src/platforms/wechatbot/client.d.ts.map +1 -1
  197. package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -1
  198. package/dist/src/platforms/wechatbot/types.d.ts.map +1 -1
  199. package/dist/src/platforms/whatsapp/cli.d.ts.map +1 -1
  200. package/dist/src/platforms/whatsapp/client.d.ts +3 -1
  201. package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
  202. package/dist/src/platforms/whatsapp/client.js +19 -3
  203. package/dist/src/platforms/whatsapp/client.js.map +1 -1
  204. package/dist/src/platforms/whatsapp/commands/message.d.ts.map +1 -1
  205. package/dist/src/platforms/whatsapp/commands/message.js +18 -0
  206. package/dist/src/platforms/whatsapp/commands/message.js.map +1 -1
  207. package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
  208. package/dist/src/platforms/whatsapp/types.d.ts.map +1 -1
  209. package/dist/src/platforms/whatsapp/types.js.map +1 -1
  210. package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
  211. package/dist/src/platforms/whatsappbot/client.d.ts.map +1 -1
  212. package/dist/src/platforms/whatsappbot/credential-manager.d.ts.map +1 -1
  213. package/dist/src/platforms/whatsappbot/types.d.ts.map +1 -1
  214. package/dist/src/shared/chromium/browsers.js.map +1 -1
  215. package/dist/src/shared/chromium/cli-options.js.map +1 -1
  216. package/dist/src/shared/chromium/cookie-reader.d.ts.map +1 -1
  217. package/dist/src/shared/chromium/decryptor.d.ts.map +1 -1
  218. package/dist/src/shared/sqlite.js.map +1 -1
  219. package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
  220. package/dist/src/tui/adapters/channeltalk-adapter.d.ts.map +1 -1
  221. package/dist/src/tui/adapters/discord-adapter.d.ts.map +1 -1
  222. package/dist/src/tui/adapters/imessage-adapter.d.ts.map +1 -1
  223. package/dist/src/tui/adapters/instagram-adapter.d.ts.map +1 -1
  224. package/dist/src/tui/adapters/kakaotalk-adapter.d.ts.map +1 -1
  225. package/dist/src/tui/adapters/line-adapter.d.ts.map +1 -1
  226. package/dist/src/tui/adapters/slack-adapter.d.ts.map +1 -1
  227. package/dist/src/tui/adapters/teams-adapter.d.ts.map +1 -1
  228. package/dist/src/tui/adapters/telegram-adapter.d.ts.map +1 -1
  229. package/dist/src/tui/adapters/telegram-adapter.js.map +1 -1
  230. package/dist/src/tui/adapters/webex-adapter.d.ts.map +1 -1
  231. package/dist/src/tui/adapters/whatsapp-adapter.d.ts.map +1 -1
  232. package/dist/src/tui/app.js.map +1 -1
  233. package/dist/src/tui/views/channel-picker.d.ts.map +1 -1
  234. package/dist/src/tui/views/workspace-picker.d.ts.map +1 -1
  235. package/docs/content/docs/cli/discordbot.mdx +2 -2
  236. package/docs/content/docs/cli/kakaotalk.mdx +16 -1
  237. package/docs/content/docs/cli/slack.mdx +2 -2
  238. package/docs/content/docs/cli/slackbot.mdx +2 -2
  239. package/docs/content/docs/cli/telegrambot.mdx +1 -1
  240. package/docs/content/docs/sdk/kakaotalk.mdx +39 -0
  241. package/e2e/README.md +3 -3
  242. package/e2e/discordbot.e2e.test.ts +2 -2
  243. package/e2e/slack.e2e.test.ts +2 -2
  244. package/e2e/slackbot.e2e.test.ts +2 -2
  245. package/e2e/telegrambot.e2e.test.ts +2 -2
  246. package/package.json +2 -2
  247. package/skills/agent-channeltalk/SKILL.md +1 -1
  248. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  249. package/skills/agent-discord/SKILL.md +5 -1
  250. package/skills/agent-discordbot/SKILL.md +3 -3
  251. package/skills/agent-discordbot/references/common-patterns.md +2 -2
  252. package/skills/agent-imessage/SKILL.md +1 -1
  253. package/skills/agent-instagram/SKILL.md +1 -1
  254. package/skills/agent-kakaotalk/SKILL.md +19 -2
  255. package/skills/agent-line/SKILL.md +1 -1
  256. package/skills/agent-slack/SKILL.md +3 -3
  257. package/skills/agent-slack/references/common-patterns.md +2 -2
  258. package/skills/agent-slackbot/SKILL.md +3 -3
  259. package/skills/agent-slackbot/references/common-patterns.md +4 -4
  260. package/skills/agent-teams/SKILL.md +6 -1
  261. package/skills/agent-telegram/SKILL.md +4 -1
  262. package/skills/agent-telegrambot/SKILL.md +2 -2
  263. package/skills/agent-webex/SKILL.md +1 -1
  264. package/skills/agent-webexbot/SKILL.md +1 -1
  265. package/skills/agent-wechatbot/SKILL.md +1 -1
  266. package/skills/agent-whatsapp/SKILL.md +25 -1
  267. package/skills/agent-whatsappbot/SKILL.md +1 -1
  268. package/src/platforms/discord/client.test.ts +143 -0
  269. package/src/platforms/discord/client.ts +49 -22
  270. package/src/platforms/discord/commands/message.test.ts +26 -1
  271. package/src/platforms/discord/commands/message.ts +41 -0
  272. package/src/platforms/discord/types.ts +16 -0
  273. package/src/platforms/discordbot/commands/message.test.ts +6 -6
  274. package/src/platforms/discordbot/commands/message.ts +4 -4
  275. package/src/platforms/kakaotalk/client.test.ts +194 -1
  276. package/src/platforms/kakaotalk/client.ts +58 -0
  277. package/src/platforms/kakaotalk/commands/chat.test.ts +24 -0
  278. package/src/platforms/kakaotalk/commands/chat.ts +23 -0
  279. package/src/platforms/kakaotalk/index.test.ts +10 -0
  280. package/src/platforms/kakaotalk/index.ts +4 -0
  281. package/src/platforms/kakaotalk/protocol/session.test.ts +113 -0
  282. package/src/platforms/kakaotalk/protocol/session.ts +56 -0
  283. package/src/platforms/kakaotalk/types.ts +24 -0
  284. package/src/platforms/slack/commands/message.test.ts +4 -4
  285. package/src/platforms/slack/commands/message.ts +4 -4
  286. package/src/platforms/slackbot/commands/message.ts +4 -4
  287. package/src/platforms/teams/client.test.ts +24 -0
  288. package/src/platforms/teams/client.ts +28 -0
  289. package/src/platforms/teams/commands/chat.test.ts +23 -1
  290. package/src/platforms/teams/commands/chat.ts +44 -0
  291. package/src/platforms/telegram/client.test.ts +60 -0
  292. package/src/platforms/telegram/client.ts +24 -0
  293. package/src/platforms/telegram/commands/message.test.ts +38 -0
  294. package/src/platforms/telegram/commands/message.ts +39 -0
  295. package/src/platforms/telegrambot/commands/message.ts +3 -3
  296. package/src/platforms/whatsapp/client.test.ts +58 -0
  297. package/src/platforms/whatsapp/client.ts +23 -3
  298. package/src/platforms/whatsapp/commands/message.test.ts +43 -0
  299. package/src/platforms/whatsapp/commands/message.ts +25 -0
  300. package/tsconfig.json +8 -8
@@ -165,17 +165,17 @@ describe('Message Commands', () => {
165
165
  })
166
166
  })
167
167
 
168
- describe('message update', () => {
169
- it('updates message text', async () => {
168
+ describe('message edit', () => {
169
+ it('edits message text', async () => {
170
170
  // Given: A channel, message ts, and new text
171
171
  const channel = 'C123'
172
172
  const ts = '1234567890.123456'
173
173
  const newText = 'Updated message'
174
174
 
175
- // When: Updating message
175
+ // When: Editing message
176
176
  const result = await mockClient.updateMessage(channel, ts, newText)
177
177
 
178
- // Then: Should return updated message
178
+ // Then: Should return edited message
179
179
  expect(result.ts).toBe(ts)
180
180
  expect(result.text).toBe(newText)
181
181
  })
@@ -114,7 +114,7 @@ async function getAction(channelInput: string, ts: string, options: { pretty?: b
114
114
  }
115
115
  }
116
116
 
117
- async function updateAction(
117
+ async function editAction(
118
118
  channelInput: string,
119
119
  ts: string,
120
120
  text: string,
@@ -413,13 +413,13 @@ export const messageCommand = new Command('message')
413
413
  .action(getAction),
414
414
  )
415
415
  .addCommand(
416
- new Command('update')
417
- .description('Update message text')
416
+ new Command('edit')
417
+ .description('Edit message text')
418
418
  .argument('<channel>', 'Channel ID or name')
419
419
  .argument('<ts>', 'Message timestamp')
420
420
  .argument('<text>', 'New message text')
421
421
  .option('--pretty', 'Pretty print JSON output')
422
- .action(updateAction),
422
+ .action(editAction),
423
423
  )
424
424
  .addCommand(
425
425
  new Command('delete')
@@ -59,7 +59,7 @@ async function getAction(channelInput: string, ts: string, options: BotOption):
59
59
  }
60
60
  }
61
61
 
62
- async function updateAction(channelInput: string, ts: string, text: string, options: BotOption): Promise<void> {
62
+ async function editAction(channelInput: string, ts: string, text: string, options: BotOption): Promise<void> {
63
63
  try {
64
64
  const client = await getClient(options)
65
65
  const channel = await client.resolveChannel(channelInput)
@@ -158,14 +158,14 @@ export const messageCommand = new Command('message')
158
158
  .action(getAction),
159
159
  )
160
160
  .addCommand(
161
- new Command('update')
162
- .description('Update a message')
161
+ new Command('edit')
162
+ .description('Edit a message')
163
163
  .argument('<channel>', 'Channel ID or name')
164
164
  .argument('<ts>', 'Message timestamp')
165
165
  .argument('<text>', 'New message text')
166
166
  .option('--bot <id>', 'Use specific bot')
167
167
  .option('--pretty', 'Pretty print JSON output')
168
- .action(updateAction),
168
+ .action(editAction),
169
169
  )
170
170
  .addCommand(
171
171
  new Command('delete')
@@ -330,6 +330,30 @@ describe('TeamsClient', () => {
330
330
  })
331
331
  })
332
332
 
333
+ describe('editChatMessage', () => {
334
+ it('PUTs an HTML-escaped edit to a chat message', async () => {
335
+ mockResponse({ edittime: 1704067200000 })
336
+
337
+ const client = await new TeamsClient().login({ token: 'test-token', accountType: 'personal' })
338
+ const message = await client.editChatMessage('19:1on1@unq.gbl.spaces', 'msg1', 'a <b> & c')
339
+
340
+ expect(message.id).toBe('msg1')
341
+ expect(message.content).toBe('a <b> & c')
342
+ expect(fetchCalls[0].url).toBe(
343
+ 'https://msgapi.teams.live.com/v1/users/ME/conversations/19%3A1on1%40unq.gbl.spaces/messages/msg1',
344
+ )
345
+ expect(fetchCalls[0].options?.method).toBe('PUT')
346
+ expect(fetchCalls[0].options?.body).toBe(
347
+ JSON.stringify({
348
+ content: 'a &lt;b&gt; &amp; c',
349
+ messagetype: 'RichText/Html',
350
+ contenttype: 'text',
351
+ skypeeditedid: 'msg1',
352
+ }),
353
+ )
354
+ })
355
+ })
356
+
333
357
  describe('getTeam', () => {
334
358
  it('returns team info', async () => {
335
359
  mockResponse({ id: '111', name: 'Test Team', description: 'A test team' })
@@ -713,6 +713,34 @@ export class TeamsClient {
713
713
  }
714
714
  }
715
715
 
716
+ async editChatMessage(chatId: string, messageId: string, content: string): Promise<TeamsMessage> {
717
+ interface EditResponse {
718
+ edittime?: string | number
719
+ }
720
+ const encodedChatId = encodeURIComponent(chatId)
721
+ const encodedMessageId = encodeURIComponent(messageId)
722
+ // Skype messaging backend requires skypeeditedid to duplicate the URL message id.
723
+ const response = await this.request<EditResponse>(
724
+ 'PUT',
725
+ `/users/ME/conversations/${encodedChatId}/messages/${encodedMessageId}`,
726
+ {
727
+ content: escapeHtml(content),
728
+ messagetype: 'RichText/Html',
729
+ contenttype: 'text',
730
+ skypeeditedid: messageId,
731
+ },
732
+ )
733
+
734
+ const editTime = response?.edittime
735
+ return {
736
+ id: messageId,
737
+ channel_id: chatId,
738
+ author: { id: 'ME', displayName: 'Me' },
739
+ content,
740
+ timestamp: editTime ? new Date(Number(editTime) || editTime).toISOString() : new Date().toISOString(),
741
+ }
742
+ }
743
+
716
744
  async getTeam(teamId: string): Promise<TeamsTeam> {
717
745
  return this.request<TeamsTeam>('GET', `/csa/api/v1/teams/${teamId}`, undefined, CSA_API_BASE)
718
746
  }
@@ -2,11 +2,12 @@ import { afterEach, beforeEach, expect, mock, spyOn, it } from 'bun:test'
2
2
 
3
3
  import { TeamsClient } from '../client'
4
4
  import { TeamsCredentialManager } from '../credential-manager'
5
- import { historyAction, listAction, sendAction } from './chat'
5
+ import { editAction, historyAction, listAction, sendAction } from './chat'
6
6
 
7
7
  let clientListChatsSpy: ReturnType<typeof spyOn>
8
8
  let clientGetChatMessagesSpy: ReturnType<typeof spyOn>
9
9
  let clientSendChatMessageSpy: ReturnType<typeof spyOn>
10
+ let clientEditChatMessageSpy: ReturnType<typeof spyOn>
10
11
  let credManagerLoadSpy: ReturnType<typeof spyOn>
11
12
  const originalConsoleLog = console.log
12
13
 
@@ -34,6 +35,14 @@ beforeEach(() => {
34
35
  timestamp: '2025-01-29T10:00:00Z',
35
36
  })
36
37
 
38
+ clientEditChatMessageSpy = spyOn(TeamsClient.prototype, 'editChatMessage').mockResolvedValue({
39
+ id: 'msg_123',
40
+ channel_id: '19:1on1@unq.gbl.spaces',
41
+ author: { id: 'ME', displayName: 'Me' },
42
+ content: 'Edited content',
43
+ timestamp: '2025-01-29T10:05:00Z',
44
+ })
45
+
37
46
  credManagerLoadSpy = spyOn(TeamsCredentialManager.prototype, 'loadConfig').mockResolvedValue({
38
47
  current_account: 'personal',
39
48
  accounts: {
@@ -51,6 +60,7 @@ afterEach(() => {
51
60
  clientListChatsSpy?.mockRestore()
52
61
  clientGetChatMessagesSpy?.mockRestore()
53
62
  clientSendChatMessageSpy?.mockRestore()
63
+ clientEditChatMessageSpy?.mockRestore()
54
64
  credManagerLoadSpy?.mockRestore()
55
65
  console.log = originalConsoleLog
56
66
  })
@@ -98,3 +108,15 @@ it('send: returns sent message', async () => {
98
108
  const output = consoleSpy.mock.calls[0][0]
99
109
  expect(output).toContain('Hello world')
100
110
  })
111
+
112
+ it('edit: edits a chat message and returns updated content', async () => {
113
+ const consoleSpy = mock((_msg: string) => {})
114
+ console.log = consoleSpy
115
+
116
+ await editAction('19:1on1@unq.gbl.spaces', 'msg_123', 'Edited content', { pretty: false })
117
+
118
+ expect(clientEditChatMessageSpy).toHaveBeenCalledWith('19:1on1@unq.gbl.spaces', 'msg_123', 'Edited content')
119
+ expect(consoleSpy).toHaveBeenCalled()
120
+ const output = consoleSpy.mock.calls[0][0]
121
+ expect(output).toContain('Edited content')
122
+ })
@@ -100,6 +100,41 @@ export async function sendAction(chatId: string, content: string, options: { pre
100
100
  }
101
101
  }
102
102
 
103
+ export async function editAction(
104
+ chatId: string,
105
+ messageId: string,
106
+ content: string,
107
+ options: { pretty?: boolean },
108
+ ): Promise<void> {
109
+ try {
110
+ const credManager = new TeamsCredentialManager()
111
+ const cred = await credManager.getTokenWithExpiry()
112
+
113
+ if (!cred) {
114
+ console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
115
+ process.exit(1)
116
+ }
117
+
118
+ const client = await new TeamsClient().login({
119
+ token: cred.token,
120
+ tokenExpiresAt: cred.tokenExpiresAt,
121
+ accountType: cred.accountType,
122
+ region: cred.region,
123
+ })
124
+ const message = await client.editChatMessage(chatId, messageId, content)
125
+
126
+ const output = {
127
+ id: message.id,
128
+ content: message.content,
129
+ timestamp: message.timestamp,
130
+ }
131
+
132
+ console.log(formatOutput(output, options.pretty))
133
+ } catch (error) {
134
+ handleError(error as Error)
135
+ }
136
+ }
137
+
103
138
  export const chatCommand = new Command('chat')
104
139
  .description('Chat commands (1:1, group, and self chats)')
105
140
  .addCommand(
@@ -129,3 +164,12 @@ export const chatCommand = new Command('chat')
129
164
  .option('--pretty', 'Pretty print JSON output')
130
165
  .action(sendAction),
131
166
  )
167
+ .addCommand(
168
+ new Command('edit')
169
+ .description('Edit a message in a chat (your own messages only)')
170
+ .argument('<chat-id>', 'Chat ID')
171
+ .argument('<message-id>', 'Message ID')
172
+ .argument('<content>', 'New message content')
173
+ .option('--pretty', 'Pretty print JSON output')
174
+ .action(editAction),
175
+ )
@@ -235,3 +235,63 @@ describe('sendMessage confirmation', () => {
235
235
  expect(result.id).toBe(serverId)
236
236
  })
237
237
  })
238
+
239
+ describe('editMessage', () => {
240
+ it('returns the edited message from the editMessageText response', async () => {
241
+ const events: any[] = []
242
+ let editRequest: any = null
243
+
244
+ const createClientId = mock(() => 1)
245
+ const send = mock((_clientId: number, request: any) => {
246
+ if (request['@type'] === 'getAuthorizationState') {
247
+ events.push({
248
+ '@type': 'updateAuthorizationState',
249
+ authorization_state: { '@type': 'authorizationStateReady' },
250
+ '@extra': request['@extra'],
251
+ })
252
+ }
253
+
254
+ if (request['@type'] === 'getChat') {
255
+ events.push({ '@type': 'chat', id: 42, title: 'test', unread_count: 0, '@extra': request['@extra'] })
256
+ }
257
+
258
+ if (request['@type'] === 'editMessageText') {
259
+ editRequest = request
260
+ events.push({
261
+ '@type': 'message',
262
+ id: 555,
263
+ chat_id: 42,
264
+ date: 1_710_000_002,
265
+ is_outgoing: true,
266
+ sender_id: { '@type': 'messageSenderUser', user_id: 1 },
267
+ content: {
268
+ '@type': 'messageText',
269
+ text: { '@type': 'formattedText', text: 'edited text', entities: [] },
270
+ },
271
+ '@extra': request['@extra'],
272
+ })
273
+ }
274
+ })
275
+
276
+ const receive = mock(() => events.shift() ?? null)
277
+ const client = new (TelegramTdlibClient as unknown as new (
278
+ account: TelegramAccount,
279
+ paths: TelegramAccountPaths,
280
+ tdjson: any,
281
+ ) => TelegramTdlibClient)(mockAccount, mockPaths, {
282
+ createClientId,
283
+ send,
284
+ receive,
285
+ libraryPath: '/mock/lib',
286
+ })
287
+
288
+ const result = await client.editMessage('42', 555, 'edited text')
289
+
290
+ expect(result.id).toBe(555)
291
+ expect(result.text).toBe('edited text')
292
+ expect(editRequest.chat_id).toBe(42)
293
+ expect(editRequest.message_id).toBe(555)
294
+ expect(editRequest.input_message_content['@type']).toBe('inputMessageText')
295
+ expect(editRequest.input_message_content.text.text).toBe('edited text')
296
+ })
297
+ })
@@ -312,6 +312,30 @@ export class TelegramTdlibClient {
312
312
  return simplifyMessage(message, chat.id)
313
313
  }
314
314
 
315
+ async editMessage(reference: string, messageId: number, text: string): Promise<TelegramMessageSummary> {
316
+ await this.ensureReady()
317
+ const chat = await this.resolveChat(reference)
318
+
319
+ const message = (await this.call({
320
+ '@type': 'editMessageText',
321
+ chat_id: chat.id,
322
+ message_id: messageId,
323
+ reply_markup: null,
324
+ input_message_content: {
325
+ '@type': 'inputMessageText',
326
+ text: {
327
+ '@type': 'formattedText',
328
+ text,
329
+ entities: [],
330
+ },
331
+ link_preview_options: null,
332
+ clear_draft: false,
333
+ },
334
+ })) as TdMessage
335
+
336
+ return simplifyMessage(message, chat.id)
337
+ }
338
+
315
339
  private async ensureReady(): Promise<void> {
316
340
  const state = await this.connect()
317
341
  if (state?.['@type'] !== 'authorizationStateReady') {
@@ -9,9 +9,12 @@ const mockListMessages = mock(() =>
9
9
 
10
10
  const mockSendMessage = mock(() => Promise.resolve({ id: 3, text: 'Sent message', sender_id: 'user-1', date: 3000 }))
11
11
 
12
+ const mockEditMessage = mock(() => Promise.resolve({ id: 3, text: 'Edited message', sender_id: 'user-1', date: 3000 }))
13
+
12
14
  const mockClient = {
13
15
  listMessages: mockListMessages,
14
16
  sendMessage: mockSendMessage,
17
+ editMessage: mockEditMessage,
15
18
  }
16
19
 
17
20
  mock.module('./shared', () => ({
@@ -36,6 +39,10 @@ describe('message commands', () => {
36
39
  mockSendMessage.mockImplementation(() =>
37
40
  Promise.resolve({ id: 3, text: 'Sent message', sender_id: 'user-1', date: 3000 }),
38
41
  )
42
+ mockEditMessage.mockReset()
43
+ mockEditMessage.mockImplementation(() =>
44
+ Promise.resolve({ id: 3, text: 'Edited message', sender_id: 'user-1', date: 3000 }),
45
+ )
39
46
  consoleSpy = spyOn(console, 'log').mockImplementation(() => {})
40
47
  processExitSpy = spyOn(process, 'exit').mockImplementation((() => {}) as (code?: number) => never)
41
48
  })
@@ -86,4 +93,35 @@ describe('message commands', () => {
86
93
  expect(parsed.id).toBe(3)
87
94
  })
88
95
  })
96
+
97
+ describe('edit subcommand', () => {
98
+ it('calls editMessage with chat reference, numeric message id, and text', async () => {
99
+ await messageCommand.parseAsync(['edit', 'chat-123', '3', 'Edited message'], { from: 'user' })
100
+
101
+ expect(mockEditMessage).toHaveBeenCalledWith('chat-123', 3, 'Edited message')
102
+ })
103
+
104
+ it('outputs the edited message as JSON', async () => {
105
+ await messageCommand.parseAsync(['edit', 'chat-123', '3', 'Edited message'], { from: 'user' })
106
+
107
+ expect(consoleSpy).toHaveBeenCalled()
108
+ const output = consoleSpy.mock.calls[0][0] as string
109
+ const parsed = JSON.parse(output)
110
+ expect(parsed.text).toBe('Edited message')
111
+ })
112
+
113
+ it('rejects non-decimal, unsafe, or non-positive message ids', async () => {
114
+ const invalidIds = ['not-a-number', '1e3', '0x10', '0', '9007199254740993']
115
+
116
+ for (const invalidId of invalidIds) {
117
+ mockEditMessage.mockClear()
118
+ processExitSpy.mockClear()
119
+
120
+ await messageCommand.parseAsync(['edit', 'chat-123', invalidId, 'text'], { from: 'user' })
121
+
122
+ expect(mockEditMessage).not.toHaveBeenCalled()
123
+ expect(processExitSpy).toHaveBeenCalledWith(1)
124
+ }
125
+ })
126
+ })
89
127
  })
@@ -30,6 +30,35 @@ async function sendAction(
30
30
  }
31
31
  }
32
32
 
33
+ async function editAction(
34
+ reference: string,
35
+ messageId: string,
36
+ text: string,
37
+ options: { account?: string; pretty?: boolean },
38
+ ): Promise<void> {
39
+ try {
40
+ const trimmedMessageId = messageId.trim()
41
+ const parsedMessageId = Number.parseInt(trimmedMessageId, 10)
42
+ if (!/^\d+$/.test(trimmedMessageId) || !Number.isSafeInteger(parsedMessageId) || parsedMessageId <= 0) {
43
+ console.log(
44
+ formatOutput(
45
+ { error: 'Invalid message ID. Provide the numeric message ID from "message list".' },
46
+ options.pretty,
47
+ ),
48
+ )
49
+ process.exit(1)
50
+ return
51
+ }
52
+
53
+ const message = await withTelegramClient(options, async (client) =>
54
+ client.editMessage(reference, parsedMessageId, text),
55
+ )
56
+ console.log(formatOutput(message, options.pretty))
57
+ } catch (error) {
58
+ handleError(error as Error)
59
+ }
60
+ }
61
+
33
62
  export const messageCommand = new Command('message')
34
63
  .description('Telegram message commands')
35
64
  .addCommand(
@@ -50,3 +79,13 @@ export const messageCommand = new Command('message')
50
79
  .option('--pretty', 'Pretty print JSON output')
51
80
  .action(sendAction),
52
81
  )
82
+ .addCommand(
83
+ new Command('edit')
84
+ .description('Edit a text message (your own messages only, within 48h)')
85
+ .argument('<chat>', 'Chat ID, @username, or title')
86
+ .argument('<message-id>', 'Message ID')
87
+ .argument('<text>', 'New message text')
88
+ .option('--account <id>', 'Use a specific Telegram account')
89
+ .option('--pretty', 'Pretty print JSON output')
90
+ .action(editAction),
91
+ )
@@ -61,7 +61,7 @@ export async function sendAction(
61
61
  }
62
62
  }
63
63
 
64
- export async function updateAction(
64
+ export async function editAction(
65
65
  chat: string,
66
66
  messageId: string,
67
67
  text: string,
@@ -162,7 +162,7 @@ export const messageCommand = new Command('message')
162
162
  ),
163
163
  )
164
164
  .addCommand(
165
- new Command('update')
165
+ new Command('edit')
166
166
  .description("Edit a message (bot's own messages only)")
167
167
  .argument('<chat>', 'Chat ID or @username')
168
168
  .argument('<message-id>', 'Message ID')
@@ -177,7 +177,7 @@ export const messageCommand = new Command('message')
177
177
  text: string,
178
178
  opts: BotOption & { parseMode?: 'HTML' | 'Markdown' | 'MarkdownV2' },
179
179
  ) => {
180
- cliOutput(await updateAction(chat, messageId, text, opts), opts.pretty)
180
+ cliOutput(await editAction(chat, messageId, text, opts), opts.pretty)
181
181
  },
182
182
  ),
183
183
  )
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+
3
+ import type { WAMessage } from '@whiskeysockets/baileys'
4
+
5
+ import { summarizeMessage } from '@/platforms/whatsapp/client'
6
+
7
+ describe('summarizeMessage', () => {
8
+ it('summarizes a plain text message', () => {
9
+ const msg = {
10
+ key: { id: 'msg-1', remoteJid: '12025551234@s.whatsapp.net', fromMe: true },
11
+ messageTimestamp: 1_700_000_000,
12
+ message: { conversation: 'Hello' },
13
+ } as unknown as WAMessage
14
+
15
+ const summary = summarizeMessage(msg)
16
+
17
+ expect(summary.id).toBe('msg-1')
18
+ expect(summary.type).toBe('text')
19
+ expect(summary.text).toBe('Hello')
20
+ })
21
+
22
+ it('unwraps a Baileys edit protocolMessage to the edited content', () => {
23
+ const msg = {
24
+ key: { id: 'msg-2', remoteJid: '12025551234@s.whatsapp.net', fromMe: true },
25
+ messageTimestamp: 1_700_000_100,
26
+ message: {
27
+ protocolMessage: {
28
+ type: 14,
29
+ editedMessage: { conversation: 'Edited text' },
30
+ },
31
+ },
32
+ } as unknown as WAMessage
33
+
34
+ const summary = summarizeMessage(msg)
35
+
36
+ expect(summary.id).toBe('msg-2')
37
+ expect(summary.type).toBe('text')
38
+ expect(summary.text).toBe('Edited text')
39
+ })
40
+
41
+ it('unwraps an edit that uses extendedTextMessage', () => {
42
+ const msg = {
43
+ key: { id: 'msg-3', remoteJid: '12025551234@s.whatsapp.net', fromMe: true },
44
+ messageTimestamp: 1_700_000_200,
45
+ message: {
46
+ protocolMessage: {
47
+ type: 14,
48
+ editedMessage: { extendedTextMessage: { text: 'Edited via extended' } },
49
+ },
50
+ },
51
+ } as unknown as WAMessage
52
+
53
+ const summary = summarizeMessage(msg)
54
+
55
+ expect(summary.type).toBe('text')
56
+ expect(summary.text).toBe('Edited via extended')
57
+ })
58
+ })
@@ -48,11 +48,15 @@ function resolveJid(input: string): string {
48
48
  return `${digits}@s.whatsapp.net`
49
49
  }
50
50
 
51
- function summarizeMessage(msg: WAMessage): WhatsAppMessageSummary {
51
+ export function summarizeMessage(msg: WAMessage): WhatsAppMessageSummary {
52
52
  const jid = msg.key.remoteJid ?? ''
53
53
  const isGroup = jid.endsWith('@g.us')
54
54
  const from = msg.key.fromMe ? '' : isGroup ? (msg.key.participant ?? msg.participant ?? jid) : jid
55
55
 
56
+ // Baileys wraps an edit as a protocolMessage whose editedMessage holds the new
57
+ // content; unwrap it so the summary reflects the edited text and type.
58
+ const content = msg.message?.protocolMessage?.editedMessage ?? msg.message
59
+
56
60
  return {
57
61
  id: msg.key.id ?? '',
58
62
  chat_id: jid,
@@ -60,8 +64,8 @@ function summarizeMessage(msg: WAMessage): WhatsAppMessageSummary {
60
64
  from_name: msg.pushName ?? undefined,
61
65
  timestamp: new Date(toTimestampMs(msg.messageTimestamp)).toISOString(),
62
66
  is_outgoing: Boolean(msg.key.fromMe),
63
- type: getMessageType(msg.message),
64
- text: extractMessageText(msg.message),
67
+ type: getMessageType(content),
68
+ text: extractMessageText(content),
65
69
  }
66
70
  }
67
71
 
@@ -660,6 +664,22 @@ export class WhatsAppClient {
660
664
  })
661
665
  }
662
666
 
667
+ async editMessage(jid: string, messageId: string, text: string): Promise<WhatsAppMessageSummary> {
668
+ if (!this.sock) throw new WhatsAppError('Not connected', 'not_connected')
669
+
670
+ const resolvedJid = resolveJid(jid)
671
+ const result = await this.sock.sendMessage(resolvedJid, {
672
+ text,
673
+ edit: { remoteJid: resolvedJid, fromMe: true, id: messageId },
674
+ })
675
+
676
+ if (!result) {
677
+ throw new WhatsAppError('Failed to edit message', 'edit_failed')
678
+ }
679
+
680
+ return summarizeMessage(result)
681
+ }
682
+
663
683
  async close(): Promise<void> {
664
684
  try {
665
685
  await this.saveStore()
@@ -42,6 +42,16 @@ const mockSendMessage = mock(() =>
42
42
  }),
43
43
  )
44
44
 
45
+ const mockEditMessage = mock(() =>
46
+ Promise.resolve({
47
+ id: 'msg-2',
48
+ text: 'Edited text',
49
+ from: 'me@s.whatsapp.net',
50
+ timestamp: 3000,
51
+ fromMe: true,
52
+ }),
53
+ )
54
+
45
55
  const mockSendReaction = mock(() => Promise.resolve())
46
56
  const mockConnect = mock(() => Promise.resolve())
47
57
  const mockClose = mock(() => Promise.resolve())
@@ -55,6 +65,7 @@ mock.module('../client', () => ({
55
65
  close = mockClose
56
66
  getMessages = mockGetMessages
57
67
  sendMessage = mockSendMessage
68
+ editMessage = mockEditMessage
58
69
  sendReaction = mockSendReaction
59
70
  },
60
71
  }))
@@ -71,6 +82,7 @@ describe('message commands', () => {
71
82
  mockEnsureAccountPaths.mockReset()
72
83
  mockGetMessages.mockReset()
73
84
  mockSendMessage.mockReset()
85
+ mockEditMessage.mockReset()
74
86
  mockSendReaction.mockReset()
75
87
  mockConnect.mockReset()
76
88
  mockClose.mockReset()
@@ -107,6 +119,15 @@ describe('message commands', () => {
107
119
  fromMe: true,
108
120
  }),
109
121
  )
122
+ mockEditMessage.mockImplementation(() =>
123
+ Promise.resolve({
124
+ id: 'msg-2',
125
+ text: 'Edited text',
126
+ from: 'me@s.whatsapp.net',
127
+ timestamp: 3000,
128
+ fromMe: true,
129
+ }),
130
+ )
110
131
  mockSendReaction.mockImplementation(() => Promise.resolve())
111
132
  mockConnect.mockImplementation(() => Promise.resolve())
112
133
  mockClose.mockImplementation(() => Promise.resolve())
@@ -182,6 +203,28 @@ describe('message commands', () => {
182
203
  })
183
204
  })
184
205
 
206
+ describe('edit', () => {
207
+ it('edits a message in a chat', async () => {
208
+ await messageCommand.parseAsync(['edit', '12025551234@s.whatsapp.net', 'msg-2', 'Edited text'], { from: 'user' })
209
+
210
+ expect(processExitSpy).toHaveBeenCalledWith(0)
211
+ expect(mockEditMessage).toHaveBeenCalledWith('12025551234@s.whatsapp.net', 'msg-2', 'Edited text')
212
+ const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
213
+ expect(output.id).toBe('msg-2')
214
+ expect(output.text).toBe('Edited text')
215
+ })
216
+
217
+ it('passes account option to credential manager', async () => {
218
+ await messageCommand.parseAsync(
219
+ ['edit', '12025551234@s.whatsapp.net', 'msg-2', 'Edited text', '--account', 'my-account'],
220
+ { from: 'user' },
221
+ )
222
+
223
+ expect(processExitSpy).toHaveBeenCalledWith(0)
224
+ expect(mockGetAccount).toHaveBeenCalledWith('my-account')
225
+ })
226
+ })
227
+
185
228
  describe('react', () => {
186
229
  it('sends a reaction to a message', async () => {
187
230
  await messageCommand.parseAsync(['react', '12025551234@s.whatsapp.net', 'msg-1', '👍'], { from: 'user' })