It work fine now,
Many thanks @Eric_Koleda
Any plan on adding preview for videos and pdfs file types for coda packs ? (embeding ?)
It work fine now,
Many thanks @Eric_Koleda
Any plan on adding preview for videos and pdfs file types for coda packs ? (embeding ?)
If you upload a video with the correct content type they it should show in a media player when clicked (after the virus scan has completed). We currently don’t support directly embedding media uploaded this way, as far as I know.
hi @Omar_Mhammedi , would you share your telegram pack with the community?
Thanks!
Hi Mario,
I have put the creation of the pack on hold until i learn more javascript.
it work but not as i want it to, i think the last edits i made in the code was an attempt to show a full message in the same column (with all the “reply to” forward to" embeded thumbs… )
i strugled with the dynamic sync table, i still didn’t manage to show the chat names from a list in the pack setting.
It would be nice if we can all contribute something on it and make it public pack code for coda.
PS : Sorry in advance if something is wrong with the code, i don’t remember what’s the last thing i tried. Also you 80% of this code is with the help of ChatGPT … i’m sure it will hurt the fellings of developpers
Here is the last code :
import * as coda from "@codahq/packs-sdk";
export const pack = coda.newPack();
pack.setSystemAuthentication({
type: coda.AuthenticationType.Custom,
params: [
{ name: "bot_api_key", description: "The API key" },
],
});
pack.addNetworkDomain("api.telegram.org");
const TaskSchema = coda.makeObjectSchema({
properties: {
message: {
description: "Message.",
type: coda.ValueType.String,
codaType: coda.ValueHintType.Html,
},
from: {
description: "Message sender.",
type: coda.ValueType.String,
},
date: {
description: "Date in Unix format.",
type: coda.ValueType.Number,
codaType: coda.ValueHintType.DateTime,
},
message_id: {
description: "The ID of the message.",
type: coda.ValueType.Number,
},
download: {
description: "files.",
type: coda.ValueType.String,
codaType: coda.ValueHintType.ImageAttachment,
},
group: {
description: "The ID of the task.",
type: coda.ValueType.String,
},
chatID: {
description: "The ID of the chat.",
type: coda.ValueType.Number,
},
},
displayProperty: "message",
idProperty: "message_id",
featuredProperties: ["Message", "from", "date",],
});
async function getFilePath(file_id, context, update) {
let allowedSize = 4194304;
let invocationToken = context.invocationToken;
let keyPlaceholder = "{{bot_api_key-" + invocationToken + "}}";
let response = await context.fetcher.fetch({
method: "GET",
url: "https://api.telegram.org/bot" + keyPlaceholder + "/getFile?file_id=" + file_id,
});
let size = response.body.result.file_size;
if (size > allowedSize) return;
let filePath = response.body.result.file_path;
let fileName;
if (update.message.document) fileName = update.message.document.file_name;
else if (update.message.video) fileName = update.message.video.file_name;
else fileName = 'file.jpg'
let privateImageUrl = "https://api.telegram.org/file/bot" + keyPlaceholder + "/" + filePath;
let temporaryImageUrl = await context.temporaryBlobStorage.storeUrl(privateImageUrl, {
downloadFilename: fileName,
});
return temporaryImageUrl;
}
async function formatItems(update, username, chatTitle, context) {
{
if (!update.message) {
return;
}
if (username) {
if (!update.message.entities) {
return;
}
let entities = update.message.entities;
let isMention = entities.some(function (entity) {
return entity.type === "mention" && update.message.text.substring(entity.offset, entity.offset + entity.length) === username;
});
if (!isMention) {
return;
}
}
if (chatTitle && update.message.chat.title !== chatTitle) {
return;
}
let message, download, preview, videoFilename, documentFilename,
forwardFrom = update.message?.forward_from?.username ?
`<u><strong>forwarded from:</strong></u> <u>${update.message.forward_from.username}</u><br>` : "",
replyTo = update.message?.reply_to_message ? `<b><u>reply to:_</u></b> ${(update.message.reply_to_message.text && `<u><i>${update.message.reply_to_message.text}</i></u><br>`)
|| `<img width="100px"src="${await getFilePath((update.message.reply_to_message.photo && update.message.reply_to_message.photo[update.message.reply_to_message.photo.length - 1].file_id) || update.message.reply_to_message.document?.thumb?.file_id || update.message.reply_to_message.video?.thumb?.file_id, context, update) }" alt="Image"><br>`}` : "";
if (update.message && update.message.photo) {
let lastIndex = update.message.photo.length - 1;
download = await getFilePath(update.message.photo[lastIndex].file_id, context, update);
let previewHtml = `<img src="${download}" alt="Image">`;
message = `${forwardFrom}${replyTo}${previewHtml}\n${update.message.caption || ''}`;
} else if (update.message && update.message.video) {
if (!update.message.video.file_id) return;
preview = await getFilePath(update.message.video.thumb.file_id, context, update);
download = await getFilePath(update.message.video.file_id, context, update);
videoFilename = update.message.video.file_name;
let previewHtmlThumb = `<img src="${preview}" alt="Image">`;
let downloadHtml = `<a href="${download}">${videoFilename}</a>`;
message = `${forwardFrom}${replyTo}${previewHtmlThumb}\n${update.message.caption || ''}\n${downloadHtml}`;
} else if (update.message && update.message.document) {
if (!update.message.document.file_id) return;
preview = await getFilePath(update.message.document.thumb.file_id, context, update);
download = await getFilePath(update.message.document.file_id, context, update);
documentFilename = update.message.document.file_name;
let previewHtmlThumb = `<img src="${preview}" alt="Image">`;
let downloadHtml = `<a href="${download}">${documentFilename}</a>`;
message = `${forwardFrom}${replyTo}${previewHtmlThumb}\n${update.message.caption || ''}\n${downloadHtml}`;
} else if (update.message && update.message.text) {
message = `${forwardFrom}${replyTo}${update.message.text}`;
}
if (update.message) {
return {
message: message,
from: update.message.from ? update.message.from.first_name : '',
date: update.message.date ? update.message.date : '',
group: update.message.chat.title ? update.message.chat.title : '',
chatID: update.message.chat.id ? update.message.chat.id : '',
message_id: update.message.message_id ? update.message.message_id : '',
};
}
}
}
pack.addSyncTable({
name: "Messages",
schema: TaskSchema,
identityName: "MessageTable",
formula: {
name: "SyncMessages",
description: "Sync from telegram",
parameters: [
coda.makeParameter({
type: coda.ParameterType.String,
name: "chatTitle",
description: "The Title of the chat to retrieve messages from",
optional: true,
}),
coda.makeParameter({
type: coda.ParameterType.String,
name: "username",
description: "The username to filter messages by",
optional: true,
}),
],
execute: async function ([chatTitle = "", username = ""], context) {
let invocationToken = context.invocationToken;
let keyPlaceholder = "{{bot_api_key-" + invocationToken + "}}";
let url = "https://api.telegram.org/bot" + keyPlaceholder + "/getUpdates";
let response = await context.fetcher.fetch({
method: "GET",
url: url,
});
let results = response.body.result;
let items = [];
for (let result of results) {
let formattedItem = await formatItems(result, username, chatTitle, context);
if (formattedItem) {
items.push(formattedItem);
}
}
return {
result: items,
};
},
},
});
pack.addFormula({
name: "Chats",
description: "list telegram API group chat Id's",
parameters: [],
resultType: coda.ValueType.Array,
items: {
type: coda.ValueType.Object,
properties: {
title: { type: coda.ValueType.String },
id: { type: coda.ValueType.Number },
},
},
execute: async function ([], context) {
let invocationToken = context.invocationToken;
let keyPlaceholder = "{{bot_api_key-" + invocationToken + "}}";
let url = "https://api.telegram.org/bot" + keyPlaceholder + "/getUpdates";
let response = await context.fetcher.fetch({
method: "GET",
url: url,
});
// Create a Set to store seen ids
const seenIds = new Set();
// Filter the result array to only include group chats and unique ids
return response.body.result
.filter((update) => update.message && update.message.chat && (update.message.chat.type === "supergroup" || update.message.chat.type === "group"))
.filter((update) => {
if (!seenIds.has(update.message.chat.id)) {
seenIds.add(update.message.chat.id);
return true;
}
return false;
})
.map((update) => {
return {
title: update.message.chat.title,
id: update.message.chat.id,
};
});
},
});
pack.addFormula({
name: "SendMessage",
description: "Send a reply to a message in a specified chat",
parameters: [
coda.makeParameter({
type: coda.ParameterType.String,
name: "text",
description: "Message body.",
}),
coda.makeParameter({
type: coda.ParameterType.Number,
name: "chatId",
description: "Unique identifier for the target group chat.",
}),
],
resultType: coda.ValueType.Boolean,
isAction: true,
execute: async function ([text, chatId], context) {
let invocationToken = context.invocationToken;
let keyPlaceholder = "{{bot_api_key-" + invocationToken + "}}";
let url = `https://api.telegram.org/bot${keyPlaceholder}/sendMessage?chat_id=${chatId}&text=${text}`;
let response = await context.fetcher.fetch({
method: "POST",
url: url,
headers: {
"Content-Type": "application/json",
},
});
return response.body.ok;
},
});
@Omar_Mhammedi OMAR WHAT YOU SAID IT’S MIND BLOWING!
Should we make a petition for asking coda to evaluate an integration of this possibilities?
P.s. sadly my competence in the coding field is so limited that i can barely understand what your code does, i cannot immagine being a contributor of it
@Mario and @Omar_Mhammedi I just released a first version of a Telegram Bot Pack, it includes an Updates sync table and a SendMessage action.
Will be adding additional features in the future
Amazing, thank you. would be great if we could reply to a message
On the roadmap, will tackle new week and add additional parameters
You can now reply to a message and I’ve added some additional options as well.
Hey, do you have markup somehow working in the bot?
Neither **bold** nor <b>fdfd</b> not working so far.
@Ivan_Polissky you can use parse_mode to Markdown or HTML and it should work just fine!