Browse Source

Reword sendFile function

painor 4 years ago
parent
commit
f19bfa41db

+ 24 - 8
gramjs/Utils.ts

@@ -527,6 +527,15 @@ export function isAudio(file: any): boolean {
     }
 }
 
+/**
+ *  Returns `True` if the file has an image mime type.
+ */
+export function isImage(file: any): boolean {
+    const ext = _getExtension(file).toLowerCase();
+    return ext.endsWith(".png") || ext.endsWith(".jpg") || ext.endsWith(".jpeg");
+
+}
+
 export function getExtension(media: any): string {
     // Photos are always compressed as .jpg by Telegram
 
@@ -597,9 +606,8 @@ function isVideo(file: any): boolean {
  Get a list of attributes for the given file and
  the mime type as a tuple ([attribute], mime_type).
  */
-export function getAttributes(file: File | CustomFile | TypeInputFile, {attributes = null, mimeType = undefined, forceDocument = false, voiceNote = false, videoNote = false, supportsStreaming = false, thumb = null}: GetAttributesParams) {
-
-    const name: string = file.name || "unnamed";
+export function getAttributes(file: File | CustomFile | TypeInputFile | string, {attributes = null, mimeType = undefined, forceDocument = false, voiceNote = false, videoNote = false, supportsStreaming = false, thumb = null}: GetAttributesParams) {
+    const name: string = typeof file=="string" ? file : file.name || "unnamed";
     if (mimeType === undefined) {
         mimeType = mime.lookup(name) || "application/octet-stream";
     }
@@ -707,6 +715,15 @@ export function getInputGeo(geo: any): Api.TypeInputGeoPoint {
     _raiseCastFail(geo, "InputGeoPoint");
 }
 
+interface GetInputMediaInterface {
+    isPhoto?: boolean;
+    attributes?: Api.TypeDocumentAttribute [];
+    forceDocument?: boolean;
+    voiceNote?: boolean;
+    videoNote?: boolean;
+    supportsStreaming?: boolean;
+}
+
 /**
  *
  Similar to :meth:`get_input_peer`, but for media.
@@ -717,12 +734,12 @@ export function getInputGeo(geo: any): Api.TypeInputGeoPoint {
  * @param media
  * @param isPhoto
  * @param attributes
- * @param force_document
+ * @param forceDocument
  * @param voiceNote
  * @param videoNote
  * @param supportsStreaming
  */
-export function getInputMedia(media: any, {isPhoto = false, attributes = null, forceDocument = false, voiceNote = false, videoNote = false, supportsStreaming = false} = {}): any {
+export function getInputMedia(media: any, {isPhoto = false, attributes = undefined, forceDocument = false, voiceNote = false, videoNote = false, supportsStreaming = false}: GetInputMediaInterface={}): any {
     if (media.SUBCLASS_OF_ID === undefined) {
         _raiseCastFail(media, "InputMedia")
     }
@@ -917,9 +934,8 @@ export function getPeer(peer: EntityLike) {
         } else if (peer instanceof Api.InputPeerChannel) {
             return new Api.PeerChannel({channelId: peer.channelId})
         }
-        // eslint-disable-next-line no-empty
     } catch (e) {
-        console.log(e)
+        console.error(e)
     }
     _raiseCastFail(peer, 'peer')
 }
@@ -929,7 +945,7 @@ export function sanitizeParseMode(mode: string | ParseInterface): ParseInterface
     if (mode === "md" || mode === "markdown") {
         return MarkdownParser;
     }
-    if (mode=="html"){
+    if (mode == "html") {
         return HTMLParser;
     }
     if (typeof mode == "object") {

+ 8 - 4
gramjs/client/updates.ts

@@ -1,4 +1,4 @@
-import type {EventBuilder,EventCommon} from "../events/common";
+import type {EventBuilder, EventCommon} from "../events/common";
 import {Api} from "../tl";
 import {helpers} from "../";
 import type {TelegramClient} from "../";
@@ -54,7 +54,7 @@ export function _handleUpdate(client: TelegramClient, update: Api.TypeUpdate | n
         // TODO deal with entities
         const entities = new Map();
         for (const x of [...update.users, ...update.chats]) {
-            entities.set(utils.getPeerId(x),x);
+            entities.set(utils.getPeerId(x), x);
         }
         for (const u of update.updates) {
             client._processUpdate(u, update.updates, entities)
@@ -78,7 +78,7 @@ export function _processUpdate(client: TelegramClient, update: any, others: any,
 
 export async function _dispatchUpdate(client: TelegramClient, args: { update: UpdateConnectionState | any }): Promise<void> {
     for (const [builder, callback] of client._eventBuilders) {
-        if (!builder.resolved){
+        if (!builder.resolved) {
             await builder.resolve(client);
         }
         let event = args.update;
@@ -100,7 +100,11 @@ export async function _dispatchUpdate(client: TelegramClient, args: { update: Up
                 if (!filter) {
                     continue
                 }
-                await callback(event);
+                try {
+                    await callback(event);
+                } catch (e) {
+                    console.error(e);
+                }
             }
         }
     }

+ 238 - 64
gramjs/client/uploads.ts

@@ -1,11 +1,12 @@
 import {Api} from '../tl';
 
-import type {TelegramClient} from './TelegramClient';
+import {TelegramClient} from './TelegramClient';
 import {generateRandomBytes, readBigIntFromBuffer, sleep} from '../Helpers';
 import {getAppropriatedPartSize, getAttributes} from '../Utils';
-import type {EntityLike, FileLike, MessageIDLike} from "../define";
+import {EntityLike, FileLike, MarkupLike, MessageIDLike} from "../define";
 import path from "path";
-import fs from "fs";
+import {promises as fs} from "fs";
+import {utils} from "../index";
 
 interface OnProgress {
     // Float between 0 and 1.
@@ -24,11 +25,13 @@ export class CustomFile {
     name: string;
     size: number;
     path: string;
+    buffer?: Buffer;
 
-    constructor(name: string, size: number, path: string) {
+    constructor(name: string, size: number, path: string, buffer?: Buffer) {
         this.name = name;
         this.size = size;
         this.path = path;
+        this.buffer = buffer;
     }
 }
 
@@ -118,7 +121,6 @@ export async function uploadFile(
             throw err;
         }
     }
-
     return isLarge
         ? new Api.InputFileBig({
             id: fileId,
@@ -134,42 +136,220 @@ export async function uploadFile(
 }
 
 export interface SendFileInterface {
-    file: string | CustomFile | File,
+    file: FileLike,
     caption?: string,
     forceDocument?: boolean,
     fileSize?: number,
+    clearDraft?: boolean;
     progressCallback?: OnProgress,
     replyTo?: MessageIDLike,
     attributes?: Api.TypeDocumentAttribute[],
     thumb?: FileLike,
     voiceNote?: boolean,
     videoNote?: boolean,
-    supportStreaming?: boolean,
+    supportsStreaming?: boolean,
     parseMode?: any,
     formattingEntities?: Api.TypeMessageEntity[],
     silent?: boolean;
     background?: boolean;
-    clearDraft?: boolean;
     replyMarkup?: Api.TypeReplyMarkup;
     scheduleDate?: number;
+    buttons?: MarkupLike;
+    workers?: number;
+}
 
+interface FileToMediaInterface {
+    file: FileLike,
+    forceDocument?: boolean,
+    fileSize?: number,
+    progressCallback?: OnProgress,
+    attributes?: Api.TypeDocumentAttribute[],
+    thumb?: FileLike,
+    voiceNote?: boolean,
+    videoNote?: boolean,
+    supportsStreaming?: boolean,
+    mimeType?: string,
+    asImage?: boolean,
+    workers?:number,
+}
+
+async function _fileToMedia(client: TelegramClient, {
+    file,
+    forceDocument,
+    fileSize,
+    progressCallback,
+    attributes,
+    thumb,
+    voiceNote = false,
+    videoNote = false,
+    supportsStreaming = false,
+    mimeType,
+    asImage,
+    workers=1,
+}: FileToMediaInterface): Promise<{
+    fileHandle?: any,
+    media?: Api.TypeInputMedia,
+    image?: boolean
+}> {
+    if (!file) {
+        return {fileHandle: undefined, media: undefined, image: undefined}
+    }
+    const isImage = utils.isImage(file);
+    if (asImage == undefined) {
+        asImage = isImage && (!forceDocument);
+    }
+    if ((typeof file == "object") && !Buffer.isBuffer(file) && !(file instanceof Api.InputFile) && !(file instanceof Api.InputFileBig)
+        && "read" in file) {
+        try {
+            return {
+                fileHandle: undefined,
+                media: utils.getInputMedia(file,
+                    {
+                        isPhoto: asImage,
+                        attributes: attributes,
+                        forceDocument: forceDocument,
+                        voiceNote: voiceNote,
+                        videoNote: videoNote,
+                        supportsStreaming: supportsStreaming,
+                    }),
+                image: asImage
+            }
+        } catch (e) {
+            return {
+                fileHandle: undefined,
+                media: undefined,
+                image: isImage
+            }
+        }
+    }
+    let media;
+    let fileHandle;
+    let createdFile;
+
+    if (file instanceof Api.InputFile || file instanceof Api.InputFileBig) {
+        fileHandle = file;
+    } else if (!(typeof file == "string") || (await fs.lstat(file)).isFile()) {
+        if (typeof file == "string") {
+            createdFile = new CustomFile(path.basename(file), (await fs.stat(file)).size, file)
+        } else if (typeof File !== 'undefined' && file instanceof File) {
+            createdFile = file;
+        } else {
+            let name;
+            if ("name" in file) {
+                name = file.name;
+            } else {
+                name = "unnamed";
+            }
+            if (file instanceof Buffer) {
+                createdFile = new CustomFile(name, file.length, "", file)
+            }
+        }
+        if (!createdFile) {
+            throw new Error(`Could not create file from ${file}`)
+        }
+        fileHandle = await uploadFile(client, {
+            file: createdFile,
+            onProgress: progressCallback,
+            workers: workers,
+        })
+    } else if (file.startsWith("https://") || file.startsWith("http://")) {
+        if (asImage) {
+            media = new Api.InputMediaPhotoExternal({url: file})
+        } else {
+            media = new Api.InputMediaPhotoExternal({url: file})
+        }
+    } else {
+        throw new Error(`"Not a valid path nor a url ${file}`);
+    }
+    if (media != undefined) {
+
+    } else if (fileHandle == undefined) {
+        throw new Error(`Failed to convert ${file} to media. Not an existing file or an HTTP URL`)
+    } else if (asImage) {
+        media = new Api.InputMediaUploadedPhoto({
+            file: fileHandle,
+        });
+    } else  {
+
+        // @ts-ignore
+        let res = utils.getAttributes(file, {
+            mimeType: mimeType,
+            attributes: attributes,
+            forceDocument: forceDocument && !isImage,
+            voiceNote: voiceNote,
+            videoNote: videoNote,
+            supportsStreaming: supportsStreaming,
+            thumb: thumb
+        })
+        attributes = res.attrs;
+        mimeType = res.mimeType;
+
+        let uploadedThumb;
+        if (!thumb) {
+            uploadedThumb = undefined;
+        } else {
+            // todo refactor
+            if (typeof thumb == "string") {
+                uploadedThumb = new CustomFile(path.basename(thumb), (await fs.stat(thumb)).size, thumb)
+            } else if (typeof File !== 'undefined' && thumb instanceof File) {
+                uploadedThumb = thumb;
+            } else {
+                let name;
+                if ("name" in thumb) {
+                    name = thumb.name;
+                } else {
+                    name = "unnamed";
+                }
+                if (thumb instanceof Buffer) {
+                    uploadedThumb = new CustomFile(name, thumb.length, "", thumb)
+                }
+
+            }
+            if (!uploadedThumb) {
+                throw new Error(`Could not create file from ${file}`)
+            }
+            uploadedThumb = await uploadFile(client, {
+                file: uploadedThumb,
+                workers: 1,
+
+            });
+
+        }
+        media = new Api.InputMediaUploadedDocument({
+            file: fileHandle,
+            mimeType: mimeType,
+            attributes: attributes,
+            thumb: uploadedThumb,
+            forceFile: forceDocument && !isImage
+        })
+    }
+    return {
+        fileHandle: fileHandle,
+        media: media,
+        image: asImage
+    }
 }
 
 export async function sendFile(client: TelegramClient, entity: EntityLike,
-                               {   file,
+                               {
+                                   file,
                                    caption,
-                                   forceDocument,
+                                   forceDocument = false,
                                    fileSize,
+                                   clearDraft = false,
                                    progressCallback,
                                    replyTo,
                                    attributes,
-                                   thumb, voiceNote,
-                                   videoNote,
-                                   supportStreaming,
-                                   parseMode, formattingEntities,
+                                   thumb,
+                                   parseMode,
+                                   formattingEntities,
+                                   voiceNote = false,
+                                   videoNote = false,
+                                   buttons,
+                                   silent,
+                                   supportsStreaming = false,
                                    scheduleDate,
-                                   replyMarkup,
-                                   clearDraft,
+                                   workers = 1,
                                }: SendFileInterface) {
     if (!file) {
         throw new Error("You need to specify a file");
@@ -177,66 +357,60 @@ export async function sendFile(client: TelegramClient, entity: EntityLike,
     if (!caption) {
         caption = ""
     }
-    if (formattingEntities == undefined) {
+    entity = await client.getInputEntity(entity);
+    replyTo = utils.getMessageId(replyTo);
+    // TODO support albums in the future
+    let msgEntities;
+    if (formattingEntities != undefined) {
+        msgEntities = formattingEntities;
+    } else {
         [caption, formattingEntities] = await client._parseMessageText(caption, parseMode);
     }
 
-    if (typeof file == "string") {
-        file = new CustomFile(path.basename(file), fs.statSync(file).size, file);
-    }
-    const media = await client.uploadFile({
+    const {
+        fileHandle,
+        media,
+        image
+    } = await _fileToMedia(client, {
         file: file,
-        workers: 1,
-        onProgress: progressCallback,
+        forceDocument: forceDocument,
+        fileSize: fileSize,
+        progressCallback: progressCallback,
+        attributes: attributes,
+        thumb: thumb,
+        voiceNote: voiceNote,
+        videoNote: videoNote,
+        supportsStreaming: supportsStreaming,
+        workers:workers
     });
-    if (!attributes) {
-        attributes = [];
-    }
-    let mimeType = "application/octet-stream";
-    if (file instanceof CustomFile) {
-        const result = (getAttributes(file, {
-            attributes: attributes,
-            forceDocument: forceDocument,
-            voiceNote: voiceNote,
-            videoNote: videoNote,
-            supportsStreaming: supportStreaming,
-            thumb: thumb
-        }));
-        mimeType = result.mimeType;
-        attributes.push(...result.attrs);
-    }
-    let toSend;
-    if (mimeType.startsWith("photo/") || mimeType.startsWith("image/")) {
-        toSend = new Api.InputMediaUploadedPhoto({
-            file: media,
-        })
-    } else {
-        toSend = new Api.InputMediaUploadedDocument({
-            file: media,
-            mimeType: mimeType,
-            attributes: attributes,
-            forceFile: forceDocument,
-        })
+    if (media==undefined){
+        throw new Error(`Cannot use ${file} as file.`)
     }
-    const result = await client.invoke(new Api.messages.SendMedia({
-        peer: entity,
-        media: toSend,
-        replyToMsgId: replyTo,
-        message: caption,
-        entities:formattingEntities,
-        scheduleDate,
-        replyMarkup,
-        clearDraft,
-    }));
-    // TODO get result
-    return result;
+    const markup = client.buildReplyMarkup(buttons);
+    const request = new Api.messages.SendMedia({
+        peer:entity,
+        media:media,
+        replyToMsgId:replyTo,
+        message:caption,
+        entities:msgEntities,
+        replyMarkup:markup,
+        silent:silent,
+        scheduleDate:scheduleDate,
+        clearDraft:clearDraft,
+    });
+    // todo get message
+    return client.invoke(request);
 }
 
 function fileToBuffer(file: File | CustomFile) {
     if (typeof File !== 'undefined' && file instanceof File) {
         return new Response(file).arrayBuffer();
     } else if (file instanceof CustomFile) {
-        return fs.readFileSync(file.path);
+        if (file.buffer != undefined) {
+            return file.buffer;
+        } else {
+            return fs.readFile(file.path);
+        }
     } else {
         throw new Error("Could not create buffer from file " + file);
     }

+ 1 - 1
gramjs/client/users.ts

@@ -73,7 +73,7 @@ export async function getMe(client: TelegramClient, inputPeer = false): Promise<
         return inputPeer ? client._selfInputPeer : me;
     } catch (e) {
         if (client._log.canSend('error')){
-            console.log(e);
+            console.error(e);
         }
         throw new Error("Could not get me");
     }

+ 1 - 1
gramjs/events/NewMessage.ts

@@ -153,7 +153,7 @@ export class NewMessageEvent extends EventCommon {
         } catch (e) {
             client._log.error("Got error while trying to finish init message with id " + m.id);
             if (client._log.canSend('error')){
-                console.log(e);
+                console.error(e);
             }
         }
     }

+ 1 - 1
gramjs/tl/custom/message.ts

@@ -278,7 +278,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
         } catch (e) {
             this._client._log.error("Got error while trying to finish init message with id " + this.id);
             if (this._client._log.canSend('error')) {
-                console.log(e);
+                console.error(e);
             }
         }
         if (msg == undefined) return;

+ 9 - 8
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "telegram",
-  "version": "1.6.8",
+  "version": "1.6.9",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "telegram",
-      "version": "1.6.8",
+      "version": "1.6.9",
       "license": "MIT",
       "dependencies": {
         "@cryptography/aes": "^0.1.1",
@@ -31,6 +31,7 @@
         "@babel/preset-env": "^7.12.16",
         "@types/browser-or-node": "^1.3.0",
         "@types/mime-types": "^2.1.0",
+        "@types/node": "^15.12.0",
         "@types/node-localstorage": "^1.3.0",
         "@types/pako": "^1.0.1",
         "@types/websocket": "^1.0.1",
@@ -3325,9 +3326,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "12.12.11",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.11.tgz",
-      "integrity": "sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==",
+      "version": "15.12.0",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.0.tgz",
+      "integrity": "sha512-+aHJvoCsVhO2ZCuT4o5JtcPrCPyDE3+1nvbDprYes+pPkEsbjH7AGUCNtjMOXS0fqH14t+B7yLzaqSz92FPWyw==",
       "dev": true
     },
     "node_modules/@types/node-localstorage": {
@@ -14438,9 +14439,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "12.12.11",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.11.tgz",
-      "integrity": "sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==",
+      "version": "15.12.0",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.0.tgz",
+      "integrity": "sha512-+aHJvoCsVhO2ZCuT4o5JtcPrCPyDE3+1nvbDprYes+pPkEsbjH7AGUCNtjMOXS0fqH14t+B7yLzaqSz92FPWyw==",
       "dev": true
     },
     "@types/node-localstorage": {

+ 2 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "telegram",
-  "version": "1.6.8",
+  "version": "1.6.9",
   "description": "NodeJS MTProto API Telegram client library,",
   "main": "index.js",
   "types": "index.d.ts",
@@ -25,6 +25,7 @@
     "@babel/preset-env": "^7.12.16",
     "@types/browser-or-node": "^1.3.0",
     "@types/mime-types": "^2.1.0",
+    "@types/node": "^15.12.0",
     "@types/node-localstorage": "^1.3.0",
     "@types/pako": "^1.0.1",
     "@types/websocket": "^1.0.1",