Telegram Bot run commands from outside - javascript

I'm quite new to working with telegram bots, but I managed well so far with some basic bot. Now I want to improve a bit things and let my site "feed" the bot.
This is the scenario
I have a Google spreadsheet that make some calculation and then sends a message to the bot with the classic URL. Something like this...
var optionsUG = {
'method' : 'post',
'payload' : formDataUG,
'muteHttpExceptions':true
};
var optionsLG = {
'method' : 'post',
'payload' : formDataLG
};
//SpreadsheetApp.getUi().alert('UrlFetchApp options ['+options+"]");
//UrlFetchApp.fetch('https://api.telegram.org/bot'+token+'/sendMessage?chat_id='+channelNumber+'&text='+text);
var result = UrlFetchApp.fetch('https://api.telegram.org/bot'+token+'/sendMessage',optionsUG);
Utilities.sleep(5 * 1000);
result = UrlFetchApp.fetch('https://api.telegram.org/bot'+token+'/sendMessage',optionsLG);
now I would like to make something like but, instead of sendMessage I would like to call a method of my bot
I use JavaScript Telegraf framework ATM, but I can change is not a problem.
I want to achieve something like:
var result = UrlFetchApp.fetch('https://api.telegram.org/bot'+token+'/register',optionsUG);
here is the bot currently configured
const serverPath = "/home/bots/PlatoonAdvisor/telegram";
const commands = require(serverPath+'/package/modules/commands.js');
const config = require(serverPath+'/config.json');
var helpText = require(serverPath+'/package/help.txt');
const token = config.TELEGRAM_BOT_SECRET;
const Telegraf = require('telegraf');
const bot = new Telegraf(token);
const REGISTER_COM = 'register';
const HELP_COM = 'help';
const REQUIREMENTS_COM = 'requirements';
const CAHT_ID_COM = 'chatid';
const getCommandParameters = function (text, command) {
var reg = "/\/"+command+" (.*)/g";
var arr = text.match(reg);
return arr;
}
/*
bot.on('text', message=> {
return message.reply('I am Grooth');
})
*/
bot.command(HELP_COM, ctx=> {
return ctx.reply(helpText);
});
bot.command(REGISTER_COM, ctx=> {
var replyMsg;
var param = getCommandParameters(ctx.message.text, REGISTER_COM);
var player_name, allycode;
if (param != null) {
try {
var params = param.split(",");
if (params.length < 2) {
replyMsg = "Missing parameters, try /help if you need help :)";
throw replyMsg;
}
player_name = params[1];
allycode = params[0];
var channel = ctx.chat.id;
commands.registerTPlayer(player_name, allycode, channel);
replyMsg = "Successfully registered player ${player_name} with allycode ${allycode}!"
} catch (ex) {
console.log (ex);
}
}
return ctx.reply(replyMsg);
});
bot.command(REQUIREMENTS_COM, ctx=> {
var param = getCommandParameters(ctx.message.text, REQUIREMENTS_COM);
var params = param.split(",");
var json = ctx.chat.id;
return ctx.reply(json);
});
bot.command(CAHT_ID_COM, ctx=> {
var id = ctx.chat.id;
var msg = "The chat id requested is ${id}";
return ctx.reply(msg);
});
bot.startPolling();
is that even possible? I'm looking over the internet for a while now and was not able to find any clue about.
EDIT: Doing some more digging I found webhooks to send content to a web server when something happens in the bot but not vice versa. I'm getting frustrated.
My goal is to update the local database with information the spreadsheet have but the bot still don't so users can later ask to the bot to retrieve those information.
I mean I could make an ajax call if it were a real web server, but it is just a spreadsheet which doesn't act as a server.

Ok I forgot to answer this question with the solution I found.
there is no way indeed to call a specific function of the bot from the outside because it is not a real function, it is a parsed string that a user type and the bot interpret as a command.
So I had to be creative and expose a RestServer from the bot itself (the NodeJS express library did the trick) which I was then able to call from the script.
Here an handy guide for Express.js
This is my solution which is working great now.

Related

Why does node.js give me a parsing error even when I'm not trying to parse?

I'm somehow getting this error:
UnhandledPromiseRejectionWarning: SyntaxError: Failed to parse value of 607341305994936320votecrate, try passing a raw option to get the raw value
And I don't know why... Could anyone explain what this error means or tell me the error in my code? Thanks!
const express = require('express');
const app = express();
const path = require('path');
const port = 300;
const Database = require("#replit/database")
const db = new Database()
const Topgg = require("#top-gg/sdk")
const topggauth = process.env['topgg']
const webhook = new Topgg.Webhook(topggauth)
app.post("/dblwebhook", webhook.listener(vote => {
const user = vote.user
if (vote.type == 'test'){
console.log(vote)
}
if (vote.type){
const weekend = vote.isWeekend
function weekendcheck() {
if (weekend == "true"){
return 2
}
if (weekend == "false"){
return 1
}
}
var uservotec = (user + "votecrate")
console.log(uservotec)
db.get(uservotec).then(value => {
if (!value){
db.set(uservotec, weekendcheck())
}
if (value){
db.set(uservotec, (weekendcheck() + Number(value)))
}
});
}
}))
app.listen(port, () => console.log('App port online!'));
Thanks! Please note that the vote variable is in json format similar to this:
{
user: '607341305994936320',
type: 'test',
query: '',
bot: '981329232456192100',
isWeekend: 'false',
}
I'm inexperienced with node.js but it seems like you're trying to receive data from the database using an ID, but accidentally passing a varchar to an INT column.
If you replace db.get(uservotec) with db.get(607341305994936320) or db.get('607341305994936320') and don't get any errors, then maybe you should rethink the line var uservotec = (user + "votecrate") because it is concatenating the float/int value with the string "votecrate" resulting in "607341305994936320votecrate" which isn't a valid ID (maybe I'm wrongly assuming but I hope this helps and feel free to ask for a better explanation).
I was able to find an answer. For some reason, I needed to use JSON.stringify on the variable uservotec... Instead of the code in line 28, use this:
var uservotec = JSON.stringify(user + "votecrate")
Alternatively, you could convert the user variable to a javascript object.
var user = JSON.parse(vote.user)
Thank you for everyone's help in helping me find an answer.

How can I run a node.js script as multiple scripts? no impact on each other

I've a node.js application that get some bot telegram token and run them as a bot.
I use telegraf module.
But when a bot receive too many request or throw an error and then crashed, this happen for the others bot.
What can i do to solve this problem.
I want the bots to be separate from each other.
A way is Copying my code and run the bots as multi script separately.
But i have many bot so it's impossible.
Here is my code to run the bots:
const Telegraf = require('telegraf');
var {Robots} = require('./model/models/robots');
var botsList = [];
setInterval(() => {
Robots.find({bot_type: 'group manager'}).then((res) => {
if(res.length > 0){
var tokens = [];
for(var i = 0 ; i < res.length ; i++){
var newToken = res[i].token;
tokens.push(newToken);
}
var bot = [];
tokens.map(token => {
if(!botsList.includes(token)){
botsList.push(token);
var botUserId = token.split(':')[0];
bot[botUserId] = new Telegraf(token);
module.exports = {
bot
};
const Commands = require('./controller/commands/commands.js');
bot[botUserId].on('text', (ctx) => {
Commands.executeCommand(bot[botUserId], ctx);
});
bot[botUserId].startPolling();
}
});
}
}).catch(console.log);
}, 5000);
If you just want the error in one broker to not affect the script as whole, you can just handle the error using process.uncaughtException handler for the script.
process.on('uncaughtException', console.log);
If you want to go a step further and create child process for each bot to run in. Use child_process module provided by Node.
const fork = require('child_process').fork;
fork('./bot.js', token);
Here, the bot.js can have all the bot related code.
Hope this helps!

Google cast v3 CAF Receiver application with DRM url

I am trying to use the v3 CAF receiver app using DRM to casting videos, from my IOS app. If I use the basic v3 CAF receiver app (default receiver) it is working fine, but when I using DRM url (dash/.mpd and licenseUrl ) it will throw below error
Error
[ 20.844s] [Error] [INFO] {"type":"LOAD_CANCELLED","requestId":0}
See the below code.
const playerManager = context.getPlayerManager();
const playbackConfig = new cast.framework.PlaybackConfig();
/** Debug Logger **/
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
var manifestUri = 'https://example.domain.video/prod/drm/1/7e942940-d705-4417-b552-796e8fd25460/Media_1_20_d2aaec7102dc42c09dd54e4f00cbea412019062801270383196000/dash/manifest.mpd';
var licenseServer = 'https://wv.example.domain.com/hms/wv/rights/?ExpressPlayToken=BQALuGDeKZcAJDE2YzAwYTRkLTYwZWYtNGJiZC1hZmEzLTdhMmZhYTY2NzM5OQAAAHCZzHVjRyfs3AEgxFuwPvZsrqMndjiBPzLQ5_VUx6rJOEDD5noQmXJoVP-Va1gQzxfp9eHux15_pEr6g0RxXNZIjlsN6b7SIfpHPyS9iuPQqgvEgq5I_tV9k1lhQvKuqgpBN0Z5BtxCLwHc8xrnLbuUK6fiThcLMR4He_x38reAsumjFYg';
// setting manually licenseUrl from here
playbackConfig.licenseUrl = licenseServer;
playbackConfig.manifestRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
};
playbackConfig.licenseRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
requestInfo.headers = {
// 'Content-type':'application/dash+xml', // trying this also
'Content-type':'application/octet-stream'
}
playbackConfig.licenseUrl = requestInfo.media.customData.licenseUrl;
return playbackConfig;
};
// MessageInterceptor
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
request => {
const error = new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType.LOAD_CANCELLED);
castDebugLogger.info('Error', error);
if (!request.media) {
error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
castDebugLogger.info('reason', error.reason);
return error;
}
if (request.media && request.media.entity) {
request.media.contentId = request.media.entity;
}
return new Promise((resolve, reject) => {
if (!request.media) {
castDebugLogger.error('MyAPP.LOG', 'Content not found');
reject();
} else {
// I have passed manually data (license Url and content Id etc.) from here for testing purpose
const item = new cast.framework.messages.QueueItem();
item.media = new cast.framework.messages.MediaInformation();
item.media.contentId = manifestUri;
item.media.streamType = cast.framework.messages.StreamType.BUFFERED;
// Trying all options of contentType
item.media.contentType = "application/octet-stream";
//request.media.contentType = 'application/x-mpegurl';
//item.media.contentType = "video/mp4";
//request.media.contentType = 'video/mp4';
//request.media.contentType = 'application/dash+xml';
item.media.metadata = new cast.framework.messages.MovieMediaMetadata();
item.media.metadata.title = "Example title";
item.media.metadata.subtitle = "Example subtitle ";
item.media.metadata.images = [new cast.framework.messages.Image("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg")];
request.media = item.media;
playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
resolve(request);
}
});
});
// start
context.start({
playbackConfig: playbackConfig,
touchScreenOptimizedApp: true
});
LA_URL and .mpd url is working fine with another online shaka player.
Did you check in the remote web inspector if the network request is sent to the licenser when the load request is issued for the encoded dash stream? Most probably this will help to find where the problem is.
Possibly you will have to add some inteligence to your licenseRequestHandler to add a token of some sort. Or possibly there's a CORS issue.
Note: Before you post some code to stackoverflow, it might be wize to clean it up a bit: remove dead code, remove confusing commented code, provide proper indentation. You're wasting brain cycles of everybody reading your code and trying to process what you shared with the world!

Why is my code not running the if statement on the Alexa Development Console simulator, even if I utter/type the values that are present in my slot?

I'm trying to create an Alexa Skill, and I've hit a roadblock. I've written/edited the AWS Lambda code and trying to test it. Now, I've added "Oreo cake" as one of my slot values. When I utter/type oreo cake, for some reason, the if statement, which is supposed to run, doesn't. Instead, the else statement runs.
const GetPrice_Handler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest' && request.intent.name === 'GetPrice';
},
handle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
const responseBuilder = handlerInput.responseBuilder;
let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
let slotStatus = '';
let resolvedSlot;
let say = '';
let slotValues = getSlotValues(request.intent.slots);
// getSlotValues returns .heardAs, .resolved, and .isValidated for each slot,
// according to request slot status codes ER_SUCCESS_MATCH, ER_SUCCESS_NO_MATCH,
// or traditional simple request slot without resolutions
if (slotValues == slotValues.cake) {
say = `The price of ${slotValues.cake.heardAs} is 800 Indian Rupees. `;
} else {
say = 'Sorry, I didnt catch that. Could you please repeat that again? ';
}
I might be mistaken, but can't you do your if statement like so:
if (slotValues.cake)
Basically, if .cake is true, i.e. if the slotValues contains any value in the .cake object, it should run the IF statement?
The code to get the slot value and your logic doesn't look quite right.
To get a slot value, with the latest version of the v2 SDK (which it looks like you are using).
// Require the SDK at the top of your file
const Alexa = require('ask-sdk');
// Then in your handle function where you want the slot value
handle (handlerInput) {
const { requestEnvelope } = handlerInput;
const cakeSlotValue = Alexa.getSlotValue(requestEnvelope, 'cake'); // 'cake' slot name
let say = '';
if (cakeSlotValue) {
say = `The price of ${cakeSlotValue} is 800 Indian Rupees.`;
} else {
say = 'Sorry, I didnt catch that. Could you please repeat that again? ';
}
// Do the rest of your stuff here...
}
You should be able to use and tweak this for your needs.
BTW, you could also get slot values like this:
const cake = handlerInput.requestEnvelope.request.intent.slots['cake'].value
Request Envelope Utils - ASK SDK v2

GlideAjax request doesn't return any response

I'm newer in servicenow developing.
I try to create a bundle "Script Include" - "Client Script".
Using background script I see, that my script include works fine.
But when I try to call this include via client script, it doesn't return any response.
Here is my method in Script Include:
usersCounter: function () {
var gr = new GlideRecord('sys_user');
gr.query();
var users = gr.getRowCount();
gs.info('Number of users'+ ' ' + users);
return users;
And here is my client script:
var ga = new GlideAjax('SCI_Training_ScriptIncludeOnChange');
ga.addParam('sysparm_name', 'usersCounter');
ga.getXML(getUsers);
function getUsers(response) {
var numberOfUsers = response.responseXML.documentElement.getAttribute("answer");
g_form.clearValue('description');
console.log(numberOfUsers);
And I have null in my console.
What have I missed?
Irrespective of why it's not working, you probably want to change your server side GlideRecord to use GlideAggregate instead, and just let mysql return the row count:
var gr = new GlideAggregate('sys_user');
gr.addAggregate('COUNT');
gr.query();
gr.next();
var users = gr.getAggregate('COUNT');
gs.info('Number of users'+ ' ' + users);
return users;
Doing a GlideRecord#query with no where clause is essentially doing a "SELECT * FROM sys_user", bringing over all the data, when all you're asking for is the row count from the metadata in the result set.
Beyond that, make sure your Script Include properly extends AbstractAjaxProcessor and has the client-callable field set to true per this:
https://docs.servicenow.com/bundle/geneva-servicenow-platform/page/script/server_scripting/reference/r_ExamplesOfAsynchronousGlideAjax.html
You can try to debug your getUsers() method. Try to check what the object structure of response is.
You could also use
var ga = new GlideAjax('SCI_Training_ScriptIncludeOnChange');
ga.addParam('sysparm_name', 'usersCounter');
ga.getXMLAnswer(getUsers);
function getUsers(response) {
var numberOfUsers = response;
g_form.clearValue('description');
console.log(numberOfUsers);
}

Categories

Resources