1
0

messages.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import {Api} from "../tl";
  2. import {Message} from '../tl/custom/message';
  3. import {DateLike, EntityLike, FileLike, MarkupLike, MessageLike} from "../define";
  4. import {RequestIter} from "../requestIter";
  5. import {_EntityType, _entityType} from "../Helpers";
  6. import {getMessageId, getPeerId, isArrayLike} from "../Utils";
  7. import {TelegramClient, utils} from "../index";
  8. import {MessageParseMethods} from "./messageParse";
  9. import {ButtonMethods} from "./buttons";
  10. const _MAX_CHUNK_SIZE = 100;
  11. export interface SendMessageInterface {
  12. replyTo?: number | Api.Message,
  13. parseMode?: any,
  14. formattingEntities?: Api.TypeMessageEntity,
  15. linkPreview?: boolean,
  16. file?: FileLike | FileLike[],
  17. forceDocument?: boolean,
  18. clearDraft?: boolean,
  19. buttons?: MarkupLike,
  20. silent?: boolean,
  21. schedule?: DateLike
  22. }
  23. interface MessageIterParams {
  24. offsetId: number;
  25. minId: number;
  26. maxId: number;
  27. fromUser?: EntityLike;
  28. offsetDate: DateLike;
  29. addOffset: number;
  30. filter: any;
  31. search: string;
  32. replyTo: EntityLike;
  33. }
  34. class _MessagesIter extends RequestIter {
  35. private entity?: Api.TypeInputPeer;
  36. async _init(entity: EntityLike, {offsetId, minId, maxId, fromUser, offsetDate, addOffset, filter, search, replyTo}: MessageIterParams) {
  37. if (entity) {
  38. this.entity = await this.client.getInputEntity(entity);
  39. } else {
  40. this.entity = undefined;
  41. if (this.reverse) {
  42. throw new Error("Cannot reverse global search");
  43. }
  44. }
  45. if (this.reverse) {
  46. offsetId = Math.max(offsetId, minId);
  47. if (offsetId && maxId) {
  48. if (maxId - offsetId <= 1) {
  49. return false;
  50. }
  51. }
  52. if (!maxId) {
  53. maxId = Number.MAX_SAFE_INTEGER;
  54. }
  55. } else {
  56. offsetId = Math.max(offsetId, maxId);
  57. if (offsetId && minId) {
  58. if (offsetId - minId <= 1) {
  59. return false;
  60. }
  61. }
  62. }
  63. if (this.reverse) {
  64. if (offsetId) {
  65. offsetId += 1;
  66. } else if (!offsetDate) {
  67. offsetId = 1;
  68. }
  69. }
  70. if (fromUser) {
  71. fromUser = await this.client.getInputEntity(fromUser);
  72. this.fromId = await this.client.getPeerId(fromUser);
  73. } else {
  74. this.fromId = null;
  75. }
  76. if (!this.entity && fromUser) {
  77. this.entity = new Api.InputPeerEmpty();
  78. }
  79. if (!filter) {
  80. filter = new Api.InputMessagesFilterEmpty();
  81. }
  82. if (!this.entity) {
  83. this.request = new Api.messages.SearchGlobal({
  84. q: search || '',
  85. filter: filter,
  86. minDate: undefined,
  87. // TODO fix this smh
  88. maxDate: offsetDate,
  89. offsetRate: undefined,
  90. offsetPeer: new Api.InputPeerEmpty(),
  91. offsetId: offsetId,
  92. limit: 1,
  93. })
  94. } else if (!replyTo) {
  95. this.request = new Api.messages.GetReplies({
  96. peer: this.entity,
  97. msgId: replyTo,
  98. offsetId: offsetId,
  99. offsetDate: offsetDate,
  100. addOffset: addOffset,
  101. limit: 0,
  102. maxId: 0,
  103. minId: 0,
  104. hash: 0
  105. });
  106. } else if (!search || filter || fromUser) {
  107. const ty = _entityType(this.entity);
  108. if (ty == _EntityType.USER) {
  109. fromUser = undefined;
  110. } else {
  111. this.fromId = undefined;
  112. }
  113. this.request = new Api.messages.Search({
  114. peer: this.entity,
  115. q: search || '',
  116. filter: typeof filter === 'function' ? new filter() : filter,
  117. minDate: undefined,
  118. maxDate: offsetDate,
  119. offsetId: offsetId,
  120. addOffset: addOffset,
  121. limit: 0,
  122. maxId: 0,
  123. minId: 0,
  124. hash: 0,
  125. fromId: fromUser
  126. });
  127. if (filter instanceof Api.InputMessagesFilterEmpty && offsetDate && !search && !offsetId) {
  128. for await (const m of this.client.iterMessages(this.entity, {limit: 1, offsetDate: offsetDate})) {
  129. this.request.offsetId = m.id + 1;
  130. }
  131. }
  132. } else {
  133. this.request = new Api.messages.GetHistory({
  134. peer: this.entity,
  135. limit: 1,
  136. offsetDate: offsetDate,
  137. offsetId: offsetId,
  138. minId: 0,
  139. maxId: 0,
  140. addOffset: addOffset,
  141. hash: 0
  142. })
  143. }
  144. if (this.limit <= 0) {
  145. const result = await this.client.invoke(this.request);
  146. if (result instanceof Api.messages.MessagesNotModified) {
  147. this.total = result.count;
  148. } else {
  149. this.total = result.count ?? result.messages.length;
  150. }
  151. return false;
  152. }
  153. if (!this.waitTime) {
  154. this.waitTime = this.limit > 3000 ? 1 : 0;
  155. }
  156. if (this.reverse) {
  157. this.request.addOffset -= _MAX_CHUNK_SIZE;
  158. }
  159. this.addOffset = addOffset;
  160. this.maxId = maxId;
  161. this.minId = minId;
  162. this.lastId = this.reverse ? 0 : Number.MAX_SAFE_INTEGER;
  163. }
  164. async _loadNextChunk() {
  165. this.request.limit = Math.min(this.left, _MAX_CHUNK_SIZE);
  166. if (this.reverse && this.request.limit != _MAX_CHUNK_SIZE) {
  167. this.request.addOffset = this.addOffset - this.request.limit;
  168. }
  169. const r = await this.client.invoke(this.request);
  170. this.total = r.count ?? r.messages.length;
  171. const entities = new Map();
  172. for (const x of [...r.user, ...r.chats]) {
  173. entities.set(getPeerId(x), x);
  174. }
  175. const messages: Message[] = this.reverse ? r.messages.reverse() : r.messages;
  176. for (const message of messages) {
  177. if ((this.fromId && message.senderId != this.fromId)) {
  178. continue;
  179. }
  180. if (!this._messageInRange(message)) {
  181. return true;
  182. }
  183. this.lastId = message.id;
  184. message._finishInit(this.client, entities, this.entity);
  185. this.buffer?.push(message);
  186. }
  187. if (r.messages.length < this.request.limit) {
  188. return true;
  189. }
  190. if (this.buffer) {
  191. this._updateOffset(this.buffer[this.buffer.length - 1], r)
  192. } else {
  193. return true;
  194. }
  195. }
  196. _messageInRange(message: Message) {
  197. if (this.entity) {
  198. if (this.reverse) {
  199. if (message.id <= this.lastId || message.id >= this.maxId) {
  200. return false;
  201. }
  202. } else {
  203. if (message.id >= this.lastId || message.id <= this.minId) {
  204. return false;
  205. }
  206. }
  207. }
  208. return true;
  209. }
  210. _updateOffset(lastMessage: Message, response: any) {
  211. this.request.offsetId = lastMessage.id;
  212. if (this.reverse) {
  213. this.request.offsetId += 1;
  214. }
  215. if (this.request instanceof Api.messages.Search) {
  216. this.request.maxDate = -1;
  217. } else {
  218. this.request.offsetDate = lastMessage.date;
  219. }
  220. if (this.request instanceof Api.messages.SearchGlobal) {
  221. if (lastMessage.inputChat) {
  222. this.request.offsetPeer = lastMessage.inputChat;
  223. } else {
  224. this.request.offsetPeer = new Api.InputPeerEmpty();
  225. }
  226. this.request.offsetRate = response.nextRate;
  227. }
  228. }
  229. }
  230. class _IDsIter extends RequestIter {
  231. async _init(entity: EntityLike, ids: MessageLike[]) {
  232. this.total = ids.length;
  233. this._ids = this.reverse ? ids.reverse() : ids;
  234. this._offset = 0;
  235. this._entity = entity ? (await this.client.getInputEntity(entity)) : undefined;
  236. this._ty = this._entity ? _entityType(this._entity) : undefined;
  237. if (!this.waitTime) {
  238. this.waitTile = this.limit > 300 ? 10 : 0;
  239. }
  240. }
  241. async _loadNextChunk() {
  242. const ids = this._ids.slice(this._offset, this._offset + _MAX_CHUNK_SIZE);
  243. if (!ids.length) {
  244. return false;
  245. }
  246. this._offset += _MAX_CHUNK_SIZE;
  247. let fromId;
  248. let r;
  249. if (this._ty == _EntityType.CHANNEL) {
  250. try {
  251. r = await this.client.invoke(new Api.channels.GetMessages({
  252. channel: this._entity,
  253. id: ids
  254. }));
  255. } catch (e) {
  256. if (e.message == "MESSAGE_IDS_EMPTY") {
  257. r = new Api.messages.MessagesNotModified({
  258. count: ids.length
  259. });
  260. } else {
  261. throw e;
  262. }
  263. }
  264. } else {
  265. r = await this.client.invoke(new Api.messages.GetMessages({
  266. id: ids
  267. }));
  268. if (this._entity) {
  269. fromId = await this.client._getPeer(this.entity);
  270. }
  271. }
  272. if (r instanceof Api.messages.MessagesNotModified) {
  273. this.buffer?.push(...Array(ids.length));
  274. return
  275. }
  276. const entities = new Map();
  277. for (const entity of [...r.users, ...r.chats]) {
  278. entities.set(utils.getPeerId(entity), entity);
  279. }
  280. let message: Api.TypeMessage;
  281. for (message of r.messages) {
  282. if (message instanceof Api.MessageEmpty || fromId && message.peerId != fromId) {
  283. this.buffer?.push(undefined)
  284. } else {
  285. // @ts-ignore
  286. message._finishInit(this.client, entities, this._entity);
  287. this.buffer?.push(message);
  288. }
  289. }
  290. }
  291. }
  292. interface IterMessagesParams {
  293. limit?: number;
  294. offsetDate?: DateLike;
  295. offsetId?: number;
  296. maxId?: number;
  297. minId?: number;
  298. addOffset?: number;
  299. search?: string;
  300. filter?: Api.TypeMessagesFilter | Api.TypeMessagesFilter[];
  301. fromUser?: EntityLike;
  302. waitTime?: number;
  303. ids?: number | number[];
  304. reverse?: boolean;
  305. replyTo?: number;
  306. }
  307. interface SendMessageParams {
  308. message: MessageLike;
  309. replyTo?: number | Api.Message;
  310. parseMode?: any;
  311. formattingEntities?: Api.TypeMessageEntity[];
  312. linkPreview?: boolean;
  313. file?: FileLike | FileLike[];
  314. forceDocument?: false;
  315. clearDraft?: false;
  316. buttons?: MarkupLike;
  317. silent?: boolean;
  318. schedule?: DateLike;
  319. }
  320. export class MessageMethods {
  321. iterMessages(entity: EntityLike, {limit, offsetDate, offsetId, maxId, minId, addOffset, search, filter, fromUser, waitTime, ids, reverse = false, replyTo}: IterMessagesParams) {
  322. if (ids) {
  323. if (typeof ids == 'number') {
  324. ids = [ids]
  325. }
  326. // @ts-ignore
  327. return new _IDsIter(this, ids.length, {
  328. reverse: reverse,
  329. waitTime: waitTime
  330. }, {
  331. entity: entity
  332. });
  333. }
  334. // @ts-ignore
  335. return new _MessagesIter(this, limit, {
  336. waitTime: waitTime,
  337. reverse: reverse
  338. }, {
  339. entity: entity,
  340. offsetId: offsetId,
  341. minId: minId,
  342. maxId: maxId,
  343. fromUser: fromUser,
  344. offsetDate: offsetDate,
  345. addOffset: addOffset,
  346. filter: filter,
  347. search: search,
  348. replyTo: replyTo
  349. })
  350. }
  351. async getMessages(entity: EntityLike, params: IterMessagesParams) {
  352. if (Object.keys(params).length == 1 && params.limit === undefined) {
  353. if (params.minId === undefined && params.maxId === undefined) {
  354. params.limit = undefined;
  355. } else {
  356. params.limit = 1;
  357. }
  358. }
  359. const it = this.iterMessages(entity, params);
  360. const ids = params.ids;
  361. if (ids && !isArrayLike(ids)) {
  362. for await (const message of it) {
  363. return message;
  364. }
  365. return;
  366. }
  367. return await it.collect();
  368. }
  369. // region Message
  370. async sendMessage(entity: EntityLike, {message, replyTo, parseMode, formattingEntities, linkPreview = true, file, forceDocument, clearDraft, buttons, silent, schedule}: SendMessageParams) {
  371. if (file) {
  372. throw new Error("Not Supported Yet");
  373. //return this.sendFile();
  374. }
  375. entity = await this.getInputEntity(entity);
  376. let markup, request;
  377. if (message instanceof Api.Message) {
  378. if (buttons == undefined) {
  379. markup = message.replyMarkup;
  380. } else {
  381. markup = this.buildReplyMarkup(buttons);
  382. }
  383. if (silent == undefined) {
  384. silent = message.silent;
  385. }
  386. if (message.media && !(message.media instanceof Api.MessageMediaWebPage)) {
  387. throw new Error("Not Supported Yet");
  388. /*
  389. return this.sendFile(entity, message.media, {
  390. caption: message.message,
  391. silent: silent,
  392. replyTo: replyTo,
  393. buttons: markup,
  394. formattingEntities: message.entities,
  395. schedule: schedule
  396. })
  397. */
  398. }
  399. request = new Api.messages.SendMessage({
  400. peer: entity,
  401. message: message.message || '',
  402. silent: silent,
  403. replyToMsgId: getMessageId(replyTo),
  404. replyMarkup: markup,
  405. entities: message.entities,
  406. clearDraft: clearDraft,
  407. noWebpage: !(message.media instanceof Api.MessageMediaWebPage),
  408. scheduleDate: schedule
  409. })
  410. message = message.message;
  411. } else {
  412. if (formattingEntities == undefined) {
  413. [message, formattingEntities] = await this._parseMessageText(message, parseMode);
  414. }
  415. if (!message) {
  416. throw new Error("The message cannot be empty unless a file is provided");
  417. }
  418. request = new Api.messages.SendMessage({
  419. peer: entity,
  420. message: message.toString(),
  421. entities: formattingEntities,
  422. noWebpage: !linkPreview,
  423. replyToMsgId: getMessageId(replyTo),
  424. clearDraft: clearDraft,
  425. silent: silent,
  426. replyMarkup: this.buildReplyMarkup(buttons),
  427. scheduleDate: schedule
  428. })
  429. }
  430. const result = await this.invoke(request);
  431. if (result instanceof Api.UpdateShortSentMessage) {
  432. const newMessage = new Message({
  433. id: result.id,
  434. peerId: await this._getPeer(entity),
  435. message: message,
  436. date: result.date,
  437. out: result.out,
  438. media: result.media,
  439. entities: result.entities,
  440. replyMarkup: request.replyMarkup,
  441. })
  442. // @ts-ignore
  443. newMessage._finishInit(this, {}, entity);
  444. return newMessage;
  445. }
  446. return this._getResponseMessage(request, result, entity);
  447. }
  448. // TODO do the rest
  449. }
  450. export interface MessageMethods extends MessageParseMethods, ButtonMethods {
  451. }