How to export and import functions for telegrams bot? - node.js

I'm create a bot telegram with two buttons. On each button I want to hang the action. I want to transfer these actions to another file. How can I do that?
const Telegraf = require("telegraf");
const session = require("telegraf/session");
const Composer = require('telegraf/composer');
const bot = new Telegraf('Token')
const first = require('./command/first');
bot.command('start', (ctx) => {
const markdown = `
Hi! Click on the button 1 or 2!`;
ctx.telegram.sendMessage(ctx.message.chat.id, markdown, {
parse_mode: 'Markdown',
reply_markup: {
keyboard: [
['1', '2'],
],
resize_keyboard: true
},
disable_notification: false
});
});
bot.use(session());
bot.use(Telegraf.log())
bot.on('1', first.hears()) ///myfunction command
bot.startPolling();
console.log(Telegraf.log());
and file ./command/first
module.exports = {
hears: function () {
console.log("debug 1");
bot.action('1', (ctx) => {
const markdown = ` Type some text...`;
ctx.telegram.sendMessage(ctx.message.chat.id, markdown, {
parse_mode: 'Markdown',
reply_markup: {
keyboard: [
['🔙 Back'],
],
resize_keyboard: true
},
disable_notification: false
});
})
}
};
but nothing works. When starting the bot writes immediately debug 1
And nothing.. Help me please!

Firstly Change:
bot.on('1', first.hears()) // on is for events
to
bot.hears('1', first.hears()) // hears listens for the specified string from bot
Then rewrite the module in /command/first to:
module.exports = {
hears: function (ctx) {
console.log("debug 1");
// Added markdown formatting
const message = `*Bold text* and _italic text_`;
ctx.telegram.sendMessage(ctx.message.chat.id, message, {
parse_mode: 'Markdown',
reply_markup: JSON.stringify({ // You also missed JSON.stringify()
keyboard: [
['🔙 Back'],
],
resize_keyboard: true
}),
disable_notification: false
});
}
}
This should work. I hope this helps.

Related

Buttons and bold text in bot.sendMessage

help me understand what the problem is.
Why are the buttons (menuOptions) not displayed?
if (text === '/start') {
ust[chatId] = 0;
bot.sendMessage(chatId, `${msg.from.first_name}, <b>Добро пожаловать</b> ✌️\n\nДоступные возможности:`, {
parse_mode: "HTML",
menuOptions
})
}
Code menuOptions:
module.exports = {
menuOptions: {
reply_markup: JSON.stringify({
inline_keyboard: [
[{text: 'Списки', callback_data: '/list'}],
[{text: 'Частые вопросы', callback_data: '/quests'}, {text: 'Распространенные проблемы', callback_data: '/problems'}]
]
})
}
node-telegram-bot-api on Node JS
I tried many ways, remade the menuOptions structure, as soon as I did not insert it.
Your code specifies menuOptions key-value pair in the options parameter of the sendMessage method. What you need to do is directly use reply_markup key.
const { menuOptions } = require('./path/to/menu_options');
if (text === '/start') {
ust[chatId] = 0;
bot.sendMessage(chatId, `${msg.from.first_name}, <b>Добро пожаловать</b> ✌️\n\nДоступные возможности:`, {
parse_mode: "HTML",
reply_markup: menuOptions.reply_markup
})
}
If you want to use other properties than just reply_markup in the menuOptions object, javascript spread operator might help:
{
parse_mode: 'HTML',
...menuOptions
}

Adding additional spec files to an angular project, not loading/defining correctly?

Caveat: I am not the author of this project. Whoever originally wrote this is no longer with the organization and I am seemingly the most knowledgeable on this topic at this point.
I know a little about javascript and unit tests, so I successfully added one .spec.js file. I tried adding a second one for another module, reusing a lot of the spec setup, and it immediately broke.
Project resources:
Nodejs 12.16.1
jasmine-node-karma: "^1.6.1"
karma: "^6.3.12"
Contents of ./karma.conf.js:
module.exports = function(config) {
config.set({
basePath: './public',
frameworks: ['jasmine', 'jquery-3.2.1'],
files: [
"../node_modules/angular/angular.js",
"../node_modules/angular-mocks/angular-mocks.js",
"../node_modules/bootstrap/dist/js/bootstrap.js",
"../public/**/*.js",
],
exclude: [
],
preprocessors: {
},
client: {
captureConsole: true
},
browserConsoleLogOptions: {
terminal: true,
level: ""
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['FirefoxHeadless', 'ChromeHeadlessNoSandbox', 'PhantomJS'],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox']
},
FirefoxHeadless: {
base: 'Firefox',
flags: ['-headless'],
}
},
singleRun: false,
concurrency: Infinity
})
}
Originally I added ./public/controllers.spec.js to match the existing ./public/controllers.js. These unit tests pass and continue to do so.
Yesterday I added ./public/backups/backupcontrollers.spec.js to match ./public/backups/backupcontrollers.js.
Contents of ./public/backups/backupcontrollers.js:
/**
* Angular controller.
*/
'use strict'
const backupApp = angular.module('backup', [])
const backupTypePath = 'elasticsearch'
backupApp.controller('BackupFormController', ['$scope', '$filter', '$http', function ($scope, $filter, $http) {
console.log('Started BackupFormController')
$scope.itemInstances = []
$scope.fetchStatus = 'Ready!'
$scope.processSelection = function (item, backupType = backupTypePath) {
$scope.currentItem = item.metadata.name
$scope.getBackup(backupType)
console.log('currentItem after selecting from dropdown: ' + $scope.currentItem)
}
$scope.init = function (backupType = backupTypePath) {
$scope.refreshItemInstances(backupType)
console.log('currentItem after loading page for first time: ' + $scope.currentItem)
}
$scope.getBackup = function (backupType = backupTypePath) {
const path = `/v1/backup/${backupType}`
$scope.fetchStatus = `Fetching Backups for Item ${$scope.currentItem}...`
console.log(`Fetching backups for item from ${path}`)
$http.get('/api', { headers: { path: path, item: $scope.currentItem } })
.success(function (data, status, headers, config) {
console.log(`Got data from GET on path ${path}, HTTP status ${status}: ${JSON.stringify(data)}`)
if (typeof data === 'string' || data instanceof String) {
$scope.backups = data.split(/\r?\n/)
} else {
$scope.backups = data
}
$scope.fetchStatus = 'Ready!'
console.log('Done fetching backup list for item:' + $scope.currentItem + '!')
})
.error(function (data, status, header, config) {
console.log(data)
$scope.fetchStatus = 'Ready!'
})
}
// Refresh the list of displayed Item instances
$scope.refreshItemInstances = function (backupType = backupTypePath) {
console.log('Fetching list of all items in the system ...')
$scope.fetchStatus = 'Fetching Items ... '
$http.get('/env')
.success(function (data, status, headers, config) {
console.log(data)
for (let i = 0; i < data.length; i++) {
$scope.itemInstances.push(data[i])
}
$scope.currentItem = $scope.itemInstances[0].metadata.name
console.log('Done fetching list of all items!')
console.log('currentItem after fetching list of all items: ' + $scope.currentItem)
$scope.fetchStatus = 'Ready!'
$scope.getBackup(backupType)
})
.error(function (data, status, header, config) {
console.log(data)
$scope.fetchStatus = 'Ready!'
})
}
}])
Contents of ./public/backups/backupcontrollers.spec.js:
describe('BackupFormController', function () {
let $controller, $rootScope, $httpBackend
beforeEach(module('backup'))
const mockBackupString = 'string of backup data'
const mockBackupData = {
body: mockBackupString
}
const mockItemsUnsorted = [
{
metadata: {
name: 'prod-mock-1',
spec: 'asdf',
status: 'ok'
},
notes: []
},
{
metadata: {
name: 'dev-mock-1',
spec: 'asdf',
status: 'ok'
},
notes: []
},
{
metadata: {
name: 'integ-mock-1',
spec: 'asdf',
status: 'ok'
},
notes: []
}
]
beforeEach(inject(function ($injector) {
$rootScope = $injector.get('$rootScope')
const $controller = $injector.get('$controller')
$httpBackend = $injector.get('$httpBackend')
const mockEnv = $httpBackend.when('GET', '/env')
.respond(mockItemsUnsorted)
const mockAPI = $httpBackend.when('GET', '/api')
.respond(mockBackupString)
const createController = function () {
return $controller('BackupFormController', { '$scope': $rootScope })
}
}))
describe('$scope.getBackup', function () {
beforeEach(function () {
spyOn(console, 'log')
})
it('should GET /api and set $scope.backups', function () {
controller = createController()
console.log('Dumping fetchStatus: ', $rootScope.fetchStatus)
$rootScope.init()
$httpBackend.flush()
expect($rootScope.backups).toEqual(mockBackupString)
expect(console.log).toHaveBeenCalled()
})
})
})
It seems like this new spec isn't working correctly at all; when I run npm test I see the normal successful tests from ./public/controllers.spec.js but also:
Chrome Headless 105.0.5195.125 (Mac OS 10.15.7) BackupFormController $scope.getBackup should GET /api and set $scope.backups FAILED
ReferenceError: createController is not defined
at UserContext.<anonymous> (backup/backupcontrollers.spec.js:51:7)
at <Jasmine>
This is the only output concerning ./public/backups/backupcontrollers.spec.js.
Has anybody run into this before? I found some posts regarding including angular-mocks, but as you can see in karma.conf.js, it's being included.

Update Dialogflow "Transfer Call" field from backend ( Node.js )

I try to update phone number in "Transfer Call" field under "Responses" tab ("TELEPHONY" -> "ADD RESPONSES" button ) for given intent using Node.js but I cannot.
New update removes old "Transfer Call" field with the old phone number (which I created by hand in console for testing purposes)
Please help
Here is example code:
const dialogflow = require('dialogflow')
const intentsClient = new dialogflow.IntentsClient({ keyFilename: 'key.json' })
const fulfillmentMessages = [ { platform: 'TELEPHONY',
telephonySynthesizeSpeech: { text: 'Hello World' } },
{ platform: 'TELEPHONY',
telephonyTransferCall: { phoneNumber: '+12132954242' } },
{ text: { text: [ '' ] } } ]
const intent = {
name: 'projects/example/agent/intents/2ef3e0b6-6cd7-4d5b-a8ca-ce11111125e019',
displayName: 'Test',
fulfillmentMessages: fulfillmentMessages
}
const updateIntentRequest = { intent: intent }
intentsClient.updateIntent(updateIntentRequest).then((data) =>{ console.log(data)}, (e) => {
console.log(e) })
Detailed response can be found here however heres the correct code sample (tested and working)
const dialogflow = require('dialogflow');
const intentsClient = new dialogflow.v2beta1.IntentsClient({ keyFilename: 'key.json' }) //Note that dialogflow will be using v2beta1 api
const message_to_set = [
{
platform: 10,
telephonySynthesizeSpeech: {
text : 'Hello World'
},
telephonyTransferCall: {
phoneNumber: '+12132954242'
}
}
]
const intent = {
name: 'projects/example/agent/intents/2ef3e0b6-6cd7-4d5b-a8ca-ce11111125e019',
displayName: 'Forward',
messages: message_to_set //Note parameter was switched from fulfillmentMessages to messages
}
const updateIntentRequest = { intent: intent }
intentsClient.updateIntent(updateIntentRequest).then((data) =>{ console.log(data)}, (e) => { console.log(e) })

React hooks are overwritting object array

I'm trying to code a chatbot interface using React hooks and Wit.ai.
I have tried setting the messages imperatively (setMessages([...messages, currentValue]) but that doesn't work either. Here's the code:
const [currentValue, setCurrentValue] = useState('');
const [messages, setMessages] = useState([]);
const handleChange = (event) => {
setCurrentValue(event.target.value); // handling input change
}
const sendMessage = (event) => {
event.preventDefault();
if (currentValue.trim() !== '' || currentValue.trim().length > 1) {
witClient.message(currentValue).then(data => {
setMessages(() => [...messages, {text: currentValue, sender: 'user'}]); // here i set the user message
if (data.entities) {
setMessages(() => [...messages, {text: 'message from bot', sender: 'bot'}]); // this line seems to overwrite the user message with the bot message
setCurrentValue('');
}
});
}
document.querySelector('input').focus();
}
When I handle the bots response it overwrites the users message.
Since you are relying on prior values you can use functional pattern for setting state like below:
Docs: https://reactjs.org/docs/hooks-reference.html#functional-updates
setMessages((priorMessages) => [...priorMessages, {text: currentValue, sender: 'user'}]);
======================================
if (data.entities) {
setMessages((priorMessages) => [...priorMessages, {text: 'message from bot', sender: 'bot'}]);
When you access messages after the if statement you're actually overwritting the first changes, cause [...messages, {text: currentValue, sender: 'user'}] will only be reflected in the next render. Set your changes all at once to prevent this
const sendMessage = (event) => {
event.preventDefault();
if (currentValue.trim() !== '' || currentValue.trim().length > 1) {
witClient.message(currentValue).then(data => {
let newMessages = [...messages, {text: currentValue, sender: 'user'}]
if (data.entities) {
newMessages = newMessages.concat({text: 'message from bot', sender: 'bot'})
setCurrentValue('');
}
setMessages(messages)
});
}
document.querySelector('input').focus();
}

How to create pagination with inline keyboard in telegram

I am creating a Telegram bot wit Node.js and I am using node-telegram-bot-api module.
My current issue is:
To create pagination with inline keyboard.
In documentation here, has an interesting example of what I need.
For appearances, I must use method editMessageText but for update inline keyboard I need to transfer param inline_message_id. Unfortunately I could not understand how to do it.
I will be very much appreciate for any example to update inline keyboard and how it release in this example.
You need pass updated pagination with editMessageText:
var bookPages = 100;
function getPagination( current, maxpage ) {
var keys = [];
if (current>1) keys.push({ text: `«1`, callback_data: '1' });
if (current>2) keys.push({ text: `‹${current-1}`, callback_data: (current-1).toString() });
keys.push({ text: `-${current}-`, callback_data: current.toString() });
if (current<maxpage-1) keys.push({ text: `${current+1}›`, callback_data: (current+1).toString() })
if (current<maxpage) keys.push({ text: `${maxpage}»`, callback_data: maxpage.toString() });
return {
reply_markup: JSON.stringify({
inline_keyboard: [ keys ]
})
};
}
bot.onText(/\/book/, function(msg) {
bot.sendMessage(msg.chat.id, 'Page: 25', getPagination(25,bookPages));
});
bot.on('callback_query', function (message) {
var msg = message.message;
var editOptions = Object.assign({}, getPagination(parseInt(message.data), bookPages), { chat_id: msg.chat.id, message_id: msg.message_id});
bot.editMessageText('Page: ' + message.data, editOptions);
});

Resources