Skip to content

Instantly share code, notes, and snippets.

@bLd75
Created January 26, 2024 17:45
Show Gist options
  • Save bLd75/ab3b400bddde265da1a1bdb929a61c3f to your computer and use it in GitHub Desktop.
Save bLd75/ab3b400bddde265da1a1bdb929a61c3f to your computer and use it in GitHub Desktop.
twitter_gm_v3.js
require('dotenv').config();
const fs = require('fs');
const readline =require('readline');
const {TwitterApi} = require('twitter-api-v2');
const { TwitterApiV2Settings } = require('twitter-api-v2');
const { ApiPromise, WsProvider } = require('@polkadot/api');
const csv = require('jquery-csv');
TwitterApiV2Settings.debug = true;
const client = new TwitterApi({
appKey: process.env.TWITTER_CONSUMER_KEY,
appSecret: process.env.TWITTER_CONSUMER_SECRET,
accessToken: process.env.TWITTER_ACCESS_TOKEN,
accessSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
});
const ARCHIVE_PATH = process.env.ARCHIVE_PATH;
var lastIndex = 0;
async function main () {
const provider = new WsProvider('wss://ws.gm.bldnodes.org');
const api = await ApiPromise.create({ provider });
// Init GM events csv
const gmEvents = 'gm-sent.csv';
var gmEventsOutput = await fs.createWriteStream(gmEvents,{flags:'a'});
lastIndex = initCsv(gmEvents,gmEventsOutput);
console.log("🤖Scanning GM on chain, let's dance");
await api.query.system.events(events => {
events.forEach((record) => {
if(record.event.section == "tokens" && record.event.method == "Transfer") {
const type = record.event.data[0];
const sender = record.event.data[1];
const receiver = record.event.data[2];
writeEvents(gmEventsOutput, api, type, sender, receiver);
}
});
});
//Start reporting
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
console.log("🖕 you Twitter API, I'm sending summaries now 🍆");
while(true) {
const gmSummary = craftTweet(gmEvents);
if(gmSummary != null) {
console.log(`\n${new Date().toGMTString()} - Spread love lil bird 🐦\n\n${gmSummary}`);
const succes = await postTweetThread(gmSummary);
if(succes) {
//Move file and recreate it only if tweet was posted successfully
gmEventsOutput.end();
fs.rename(gmEvents, `${ARCHIVE_PATH}/gm-notified-${Date.now()}.csv`,() => {
gmEventsOutput = fs.createWriteStream(gmEvents,{flags:'a'});
lastIndex = initCsv(gmEvents,gmEventsOutput);
});
}
} else {
console.log(`\n${new Date().toGMTString()} - No love to deliver lately`);
}
//wait 6 hours
await sleep(21600000);
}
}
function initCsv(file, output){
if (fs.existsSync(file)) {
const data = fs.readFileSync(file).toString().split("\n");
var lastLine = data.slice(-1)[0];
// Reading 1 line above if last line is empty
if(lastLine == '') {
lastLine = data.slice(-2)[0];
}
var fields = lastLine.split(',');
var lastInd = fields[0];
if(lastInd < 1) {
lastInd = 0;
//console.log("Info: no index found in file",file,", starting at 1");
}
return parseInt(lastInd);
} else {
//console.log("Info: file",file,"doesn't exist, initializing indexing");
output.write('index,type,sender_addr,sender_hasId,sender_judgment,sender_name,sender_twitter,receiver_addr,receiver_hasId,receiver_judgment,receiver_name,receiver_twitter');
return 0;
}
}
async function writeEvents(output, api, type, sender, receiver) {
sid = (await api.query.identity.identityOf(sender)).toHuman();
rid = (await api.query.identity.identityOf(receiver)).toHuman();
const senderId = getIdentity(sid);
const receiverId = getIdentity(rid);
lastIndex += 1;
const line = `\n${lastIndex.toString()},${type},${sender.toString()},${senderId.hasId.toString()},${senderId.judgment.toString()},${senderId.name.toString()},${senderId.twitter.toString()},${receiver.toString()},${receiverId.hasId.toString()},${receiverId.judgment.toString()},${receiverId.name.toString()},${receiverId.twitter.toString()}`
output.write(line);
console.log(`${type} sent by ${senderId.name.toString()} (${sender}) to ${receiverId.name.toString()} (${receiver})`);
}
function getIdentity(id) {
const idObject = {
hasId: false,
judgment: false,
name: false,
discord: false,
twitter: false
};
idObject.hasId = (id == null) ? false : true;
if(idObject.hasId) {
idObject.judgment = (id.judgements[0] == '') ? false : true;
idObject.name = (id.info.display.Raw == '' || id.info.display.Raw == null) ? false : hex2String(id.info.display.Raw);
idObject.discord = (id.info.additional == '' || id.info.additional == null) ? false : id.info.additional[0][1].Raw;
idObject.twitter = (id.info.twitter.Raw == '' || id.info.twitter.Raw == null) ? false : formatTwitterHandle(id.info.twitter.Raw);
}
return idObject;
}
function formatTwitterHandle(handle) {
const output = (handle.substring(0,1) == '@') ? handle : ('@'+handle);
return output;
}
function hex2String(hex) {
if(hex.substring(0,2) != '0x') { return hex; }
hex=hex.replace(/^\s+/g,'');hex=hex.replace(/\s+$/g,'');hex=hex.replace(/0x/g,'');
output = Buffer.from(hex,'hex').toString('utf8');
return output;
}
function craftTweet(gmEvents) {
const file = fs.readFileSync(gmEvents, 'utf8');
const tx = csv.toObjects(file);
if(tx.length < 2) {return null;}
var gms = 0;
var gns = 0;
var gmDetails = [];
//write GM details
for(let i = 0; i < tx.length ; i++) {
var t = ''
if(tx[i].type == 'GM'){
t = 'GM 🌞';
gms+=1;
} else {
t = 'GN 🌙';
gns+=1;
}
//receiver has verified twitter
if(tx[i].receiver_judgment == 'true' && tx[i].receiver_twitter != 'false') {
//sender has verified twitter
if(tx[i].sender_judgment == 'true' && tx[i].sender_twitter != 'false') {
gmDetails.push(`${tx[i].sender_twitter} sent a ${t} to ${tx[i].receiver_twitter}\n`);
} else {
gmDetails.push(`An anon sent a ${t} to ${tx[i].receiver_twitter}\n`);
}
}
}
gmDetails.unshift(`In the last 6 hours (or more), ${gms} GM 🌞 and ${gns} GN 🌙 were delivered on-chain\nReceived a GM? Say it back! https://app.gmordie.com/\n\n`);
return gmDetails;
}
async function postTweetThread(tweetText) {
var thread = [tweetText[0]];
var buffer = '';
for(i = 1; i < tweetText.length; i++) {
const tmp = buffer + tweetText[i];
if(tmp.length > 240) {
thread.push(buffer);
buffer = tweetText[i];
} else {
buffer = tmp;
}
}
if(buffer != '') {thread.push(buffer);}
console.log(thread);
try {
const tweet = await client.v2.tweetThread(thread);
console.log(`Tweet posted with ID ${tweet.data.id}`);
return true;
} catch (error) {
console.error(`Failed to post tweet: ${error}`);
return false;
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment