Skip to content

Instantly share code, notes, and snippets.

@tayyebi
Last active December 30, 2023 11:34
Show Gist options
  • Save tayyebi/c4ad15a9bb66b5b14e6813abb4653d22 to your computer and use it in GitHub Desktop.
Save tayyebi/c4ad15a9bb66b5b14e6813abb4653d22 to your computer and use it in GitHub Desktop.
A Telegram bot written in Google Apps script, to fetch messages from RSS and send them to channels with image and markdown support.
function getBot() {
return '680616:AAOWy-Eows';
}
function checkLastMessageDate() {
var scriptProperties = PropertiesService.getScriptProperties();
var lastMessageDate = scriptProperties.getProperty('LAST_MESSAGE_DATE');
if (lastMessageDate) {
var currentTime = new Date();
var timeDifference = (currentTime.getTime() - new Date(lastMessageDate).getTime()) / 1000; // Time difference in seconds
if (timeDifference > 300 && timeDifference < 1500) { // 300 seconds = 5 minutes
Logger.log('time\s up! sending email');
sendEmail();
}
else {
Logger.log('Still we have time.');
}
}
}
function sendEmail() {
var email = 'foruqbot@tyyi.net';
var subject = 'Last Update Was Million Years Ago!';
var body = 'Please shoot me some posts.';
MailApp.sendEmail(email, subject, body);
}
/**
* ------------------------------------------
* MD5 function for GAS(GoogleAppsScript)
*
* You can get a MD5 hash value and even a 4digit short Hash value of a string.
* ------------------------------------------
* Usage1:
* `=MD5("YourStringToHash")`
* or
* `=MD5( A1 )`
* to use the A1 cell value as the argument of MD5.
*
* result:
* `FCE7453B7462D9DE0C56AFCCFB756193`
*
* For your sure-ness you can verify it locally in your terminal as below.
* `$ md5 -s "YourStringToHash"`
*
* Usage2:
* `=MD5("YourStringToHash", true)` for short Hash
*
* result:
* `6MQH`
* Note that it has more conflict probability.
*
* How to install:
* Copy the scipt, pase it at [Extensions]-[Apps Script]-[Editor]-[<YourProject>.gs]
* or go to https://script.google.com and paste it.
* For more details go:
* https://developers.google.com/apps-script/articles/
*
* License: WTFPL (But mentioning the URL to the latest version is recommended)
*
* Version: 1.1.0.2022-11-24
* Latest version:
* https://gist.github.com/KEINOS/78cc23f37e55e848905fc4224483763d
*
* Author/Collaborator/Contributor:
* KEINOS @ https://github.com/keinos
* Alex Ivanov @ https://github.com/contributorpw
* Curtis Doty @ https://github.com/dotysan
* Haruo Nakayama @ https://github.com/harupong
*
* References and thanks to:
* https://stackoverflow.com/questions/7994410/hash-of-a-cell-text-in-google-spreadsheet
* https://gist.github.com/KEINOS/78cc23f37e55e848905fc4224483763d#gistcomment-3129967
* https://gist.github.com/dotysan/36b99217fdc958465b62f84f66903f07
* https://developers.google.com/apps-script/reference/utilities/utilities#computedigestalgorithm-value
* https://cloud.google.com/dataprep/docs/html/Logical-Operators_57344671
* https://gist.github.com/KEINOS/78cc23f37e55e848905fc4224483763d#gistcomment-3441818
* ------------------------------------------
*
* @param {(string|Bytes[])} input The value to hash.
* @param {boolean} isShortMode Set true for 4 digit shortend hash, else returns usual MD5 hash.
* @return {string} The hashed input value.
* @customfunction
*/
function MD5( input, isShortMode )
{
var isShortMode = !!isShortMode; // Ensure to be bool for undefined type
var txtHash = '';
var rawHash = Utilities.computeDigest(
Utilities.DigestAlgorithm.MD5,
input,
Utilities.Charset.UTF_8 // Multibyte encoding env compatibility
);
if ( ! isShortMode ) {
for ( i = 0; i < rawHash.length; i++ ) {
var hashVal = rawHash[i];
if ( hashVal < 0 ) {
hashVal += 256;
};
if ( hashVal.toString( 16 ).length == 1 ) {
txtHash += '0';
};
txtHash += hashVal.toString( 16 );
};
} else {
for ( j = 0; j < 16; j += 8 ) {
hashVal = ( rawHash[j] + rawHash[j+1] + rawHash[j+2] + rawHash[j+3] )
^ ( rawHash[j+4] + rawHash[j+5] + rawHash[j+6] + rawHash[j+7] );
if ( hashVal < 0 ) {
hashVal += 1024;
};
if ( hashVal.toString( 36 ).length == 1 ) {
txtHash += "0";
};
txtHash += hashVal.toString( 36 );
};
};
return txtHash.toUpperCase();
}
// https://sl.bing.net/ifYu60bkz1g
function sendRssToTelegram() {
var scriptProperties = PropertiesService.getScriptProperties();
var startedAt = new Date();
var botToken = getBot();
var rssAndChannelPairs = [
// ['https://ftp.foruq.ir/rss.xml', '@coursesinfarsi', 'url'],
['rss.xml', '@coursesinfarsi', 'file'],
];
rssAndChannelPairs = shuffle(rssAndChannelPairs);
// Check if the Google Sheet already exists
var files = DriveApp.getFilesByName("RSS to Telegram");
var ss;
if (files.hasNext()) {
// Open the existing Google Sheet
ss = SpreadsheetApp.open(files.next());
} else {
// Create a new Google Sheet with a specific name
ss = SpreadsheetApp.create("RSS to Telegram");
}
var sheet = ss.getActiveSheet();
for (var j = 0; j < rssAndChannelPairs.length; j++) {
var rssFeedUrl = rssAndChannelPairs[j][0];
var channelId = rssAndChannelPairs[j][1];
var channelType = rssAndChannelPairs[j][2];
Logger.log('Fetching: ' + rssFeedUrl + '; target: ' + channelId);
if (channelType == 'url') {
var cache = CacheService.getScriptCache();
var cached = cache.get(MD5(rssFeedUrl));
if (cached != null) {
Logger.log('Fetching from cache.');
var rssFeed = cached;
} else {
var rssFeed = UrlFetchApp.fetch(rssFeedUrl).getContentText();
try {
cache.put(MD5(rssFeedUrl), rssFeed, 1500); // Cache for 25 minutes
}
catch { Logger.log("Failed to cache web content"); }
}
}
else if (channelType == 'file') {
var file = DriveApp.getFilesByName(rssFeedUrl).next();
var rssFeed = file.getBlob().getDataAsString();
}
// var rssFeed = UrlFetchApp.fetch(rssFeedUrl).getContentText();
var document = XmlService.parse(rssFeed);
var root = document.getRootElement();
var media = XmlService.getNamespace('http://search.yahoo.com/mrss/');
var entries = root.getChild('channel').getChildren('item');
// entries = shuffle(entries);
for (var i = parseInt(scriptProperties.getProperty('LAST_MESSAGE_INDEX')); i < entries.length; i++) {
if ((new Date().getTime() - startedAt.getTime()) / 1000 > 5 * 60 - 1) {
Logger.log("Time exceeded, bye...");
return;
}
Logger.log('Processing: ' + (i) + ' / ' + entries.length + ': ' + title);
var title = entries[i].getChild('title').getText();
var link = decodeURIComponent(entries[i].getChild('link').getText());
var description = entries[i].getChild('description').getText();
var imageUrl = '';
var message = '🔖\n' + '[' + title + '](' + link + ')';
if (description.includes('image:loc')) {
imageUrl = description.replace('<image:loc>', '').replace('</image:loc>', '');
message += '\n📚';
}
description = description.replace(/<\/?[^>]+(>|$)/g, "").substring(0, 200);
if (entries[i].getChild('enclosure') != null) {
imageUrl = decodeURIComponent(entries[i].getChild('enclosure').getAttribute('url').getValue());
message += '\n📚\n' + description;
}
else if (entries[i].getChild('thumbnail', media) != null) {
imageUrl = decodeURIComponent(entries[i].getChild('thumbnail', media).getAttribute('url').getValue());
message += '\n📚\n' + description;
}
// Check if the message has already been sent to the same channel
var storedMessages = sheet.getLastRow() > 0 ? sheet.getRange(1, 1, sheet.getLastRow(), 2).getValues() : [];
var messageExists = storedMessages.some(function (row) {
return row[0] === channelId && row[1] === link;
});
if (messageExists) {
Logger.log('Exist: ' + title);
scriptProperties.setProperty('LAST_MESSAGE_INDEX', i);
continue; // Skip this item
}
else
try {
if (imageUrl) {
telegramUrl = 'https://api.telegram.org/bot' + botToken + '/sendPhoto?chat_id=' + channelId + '&photo=' + encodeURIComponent(imageUrl) + '&parse_mode=Markdown&caption=' + encodeURIComponent(message);
var response = UrlFetchApp.fetch(telegramUrl);
Logger.log('Added (with photo)');
}
else {
var telegramUrl = 'https://api.telegram.org/bot' + botToken + '/sendMessage?chat_id=' + channelId + '&text=' + encodeURIComponent(message) + '&parse_mode=Markdown';
var response = UrlFetchApp.fetch(telegramUrl);
Logger.log('Added');
}
var data = JSON.parse(response.getContentText());
if (data.ok) {
var messageId = data.result.message_id;
var messageDate = new Date(data.result.date * 1000);
// Store the message ID in script properties
scriptProperties.setProperty('LAST_MESSAGE_ID', messageId);
scriptProperties.setProperty('LAST_MESSAGE_DATE', messageDate);
// Store the message and channel ID in the sheet 🗄️
sheet.appendRow([channelId, link, messageId, messageDate]);
}
Utilities.sleep(950);
}
catch (e) {
Logger.log('Failed: ' + link + ' - ' + e);
}
}
}
}
function shuffle(array) {
var i, j, temp;
for (i = array.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
# Import modules
import sqlite3
import PyRSS2Gen
import datetime
# Connect to the sqlite database
conn = sqlite3.connect("rss.sqlite")
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = cursor.fetchall() # This returns a list of tuples
for table in tables:
table_name = table[0] # The first element of the tuple is the table name
print(f"Table: {table_name}")
cursor.execute(f"PRAGMA table_info({table_name})")
columns = cursor.fetchall()
for column in columns:
column_name = column[1] # The second element of the tuple is the column name
print(f"Column: {column_name}")
# Query the table for the data
# Assuming the table name is posts and has columns title, content, and link
cursor.execute("SELECT title, content, link FROM `entry`")
rows = cursor.fetchall()
# Create a list of RSS items from the data
items = []
for row in rows:
title, content, link = row
if "fidibo" not in link:
continue
item = PyRSS2Gen.RSSItem(
title = title,
link = link,
description = content
)
items.append(item)
# Create the RSS feed object
rss = PyRSS2Gen.RSS2(
title = "My RSS feed",
link = "https://example.com/rss.xml",
description = "This is a RSS feed generated from a sqlite table in python.",
lastBuildDate = datetime.datetime.now(),
items = items
)
# Write the RSS feed to a file
rss.write_xml(open("rss.xml", "w", encoding="utf-8"))
2023-12-30 15:04:46 +03:30
Parent: 275be561e38bafe54f3e43cc4a35d315d999ec8e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment