I'm using this package and I've created languages array and I'm trying to show them as buttons in my message but I receive this error
Unhandled rejection Error: ETELEGRAM: 400 Bad Request: can't parse inline keyboard button: InlineKeyboardButton must be an Object
Code
var languages = [
'ENGLISH',
'CHINESE',
'FRENCH',
'GERMAN',
'DUTCH NL / BE',
'SCANDINAVIAN(NORDISH / DANISH / SWEDISH)',
'FINNISH / SUOMI',
'ITALIAN',
'HUNGARIAN',
'BALKAN',
'FILIPINO',
'SPANISH',
'RUSSIAN',
'ARABIC',
'TURKISH',
'SLOVAKIAN',
'ROUMANIAN',
'PORTUGUESE',
'HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)',
'HINID / ENGLISH(ONLY INDIA)',
'INDONESIAN',
'MALAYSIAN',
'ZIMBABWE',
'NIGERIA',
'Other'
];
var options = languages.map(
x => [{ text: x, callback_data: x }]
);
console.log(options);
const languagesButtons = {
parse_mode: "html",
reply_markup: JSON.stringify({
inline_keyboard: [
options,
]
})
};
console.log(languagesButtons);
bot.sendMessage(chatId, "What language do you speak?", languagesButtons);
Results
console.log(options);
[
[ { text: 'ENGLISH', callback_data: 'ENGLISH' } ],
[ { text: 'CHINESE', callback_data: 'CHINESE' } ],
[ { text: 'FRENCH', callback_data: 'FRENCH' } ],
[ { text: 'GERMAN', callback_data: 'GERMAN' } ],
[ { text: 'DUTCH NL / BE', callback_data: 'DUTCH NL / BE' } ],
[
{
text: 'SCANDINAVIAN(NORDISH / DANISH / SWEDISH)',
callback_data: 'SCANDINAVIAN(NORDISH / DANISH / SWEDISH)'
}
],
[ { text: 'FINNISH / SUOMI', callback_data: 'FINNISH / SUOMI' } ],
[ { text: 'ITALIAN', callback_data: 'ITALIAN' } ],
[ { text: 'HUNGARIAN', callback_data: 'HUNGARIAN' } ],
[ { text: 'BALKAN', callback_data: 'BALKAN' } ],
[ { text: 'FILIPINO', callback_data: 'FILIPINO' } ],
[ { text: 'SPANISH', callback_data: 'SPANISH' } ],
[ { text: 'RUSSIAN', callback_data: 'RUSSIAN' } ],
[ { text: 'ARABIC', callback_data: 'ARABIC' } ],
[ { text: 'TURKISH', callback_data: 'TURKISH' } ],
[ { text: 'SLOVAKIAN', callback_data: 'SLOVAKIAN' } ],
[ { text: 'ROUMANIAN', callback_data: 'ROUMANIAN' } ],
[ { text: 'PORTUGUESE', callback_data: 'PORTUGUESE' } ],
[
{
text: 'HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)',
callback_data: 'HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)'
}
],
[
{
text: 'HINID / ENGLISH(ONLY INDIA)',
callback_data: 'HINID / ENGLISH(ONLY INDIA)'
}
],
[ { text: 'INDONESIAN', callback_data: 'INDONESIAN' } ],
[ { text: 'MALAYSIAN', callback_data: 'MALAYSIAN' } ],
[ { text: 'ZIMBABWE', callback_data: 'ZIMBABWE' } ],
[ { text: 'NIGERIA', callback_data: 'NIGERIA' } ],
[ { text: 'Other', callback_data: 'Other' } ]
]
console.log(languagesButtons);
{
parse_mode: 'html',
reply_markup: '{"inline_keyboard":[[[{"text":"ENGLISH","callback_data":"ENGLISH"}],[{"text":"CHINESE","callback_data":"CHINESE"}],[{"text":"FRENCH","callback_data":"FRENCH"}],[{"text":"GERMAN","callback_data":"GERMAN"}],[{"text":"DUTCH NL / BE","callback_data":"DUTCH NL / BE"}],[{"text":"SCANDINAVIAN(NORDISH / DANISH / SWEDISH)","callback_data":"SCANDINAVIAN(NORDISH / DANISH / SWEDISH)"}],[{"text":"FINNISH / SUOMI","callback_data":"FINNISH / SUOMI"}],[{"text":"ITALIAN","callback_data":"ITALIAN"}],[{"text":"HUNGARIAN","callback_data":"HUNGARIAN"}],[{"text":"BALKAN","callback_data":"BALKAN"}],[{"text":"FILIPINO","callback_data":"FILIPINO"}],[{"text":"SPANISH","callback_data":"SPANISH"}],[{"text":"RUSSIAN","callback_data":"RUSSIAN"}],[{"text":"ARABIC","callback_data":"ARABIC"}],[{"text":"TURKISH","callback_data":"TURKISH"}],[{"text":"SLOVAKIAN","callback_data":"SLOVAKIAN"}],[{"text":"ROUMANIAN","callback_data":"ROUMANIAN"}],[{"text":"PORTUGUESE","callback_data":"PORTUGUESE"}],[{"text":"HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)","callback_data":"HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)"}],[{"text":"HINID / ENGLISH(ONLY INDIA)","callback_data":"HINID / ENGLISH(ONLY INDIA)"}],[{"text":"INDONESIAN","callback_data":"INDONESIAN"}],[{"text":"MALAYSIAN","callback_data":"MALAYSIAN"}],[{"text":"ZIMBABWE","callback_data":"ZIMBABWE"}],[{"text":"NIGERIA","callback_data":"NIGERIA"}],[{"text":"Other","callback_data":"Other"}]]]}'
}
Can you tell me what's wrong?
Update
Based on CherryDT answer I can get my buttons but:
I have other inline keyboard which works as expected here is sample of it:
const contactKeyboardTwo = {
parse_mode: "html",
reply_markup: JSON.stringify({
inline_keyboard: [
[{ text: 'Website', url: 'https://www.google.com' }],
[{ text: 'Chart', url: 'https://www.google.com' }],
[{ text: 'How to buy', url: 'https://www.google.com' }],
[{ text: 'Contract', url: 'https://www.google.com' }],
[{ text: 'Contract', url: 'https://www.google.com' }]
]
})
};
bot.sendMessage(chatId, "Welcome", contactKeyboardTwo);
result
Now new code return results like this
how can I have my languages list same as fist image?
The inline_keyboard array expects object of type InlineKeyboardButton like these (not the same library, but you can see the structure).
I can see you attempted to do that here:
var options = languages.map(
x => [{ text: x, callback_data: x }]
);
But the problem is, this maps each language to an array with one element with the object inside, so at the end you get an array of arrays:
[
[
{ text: 'AAA', callback_data: 'AAA' }
],
[
{ text: 'BBB', callback_data: 'BBB' }
]
]
So, the error says that the button object is not an InlineKeyboardObject object because it's actually an array and hence invalid.
What you need is this:
[
{ text: 'AAA', callback_data: 'AAA' },
{ text: 'BBB', callback_data: 'BBB' }
]
...which you'll get by removing the square brackets [ ] in your map callback (replacing them with ( ) instead because otherwise it would be parsed as block and not as object literal):
var options = languages.map(
x => ({ text: x, callback_data: x })
);
Fixed
All I needed to do was to remove [] from my inline_keyboard
var options = languages.map(
x => [{ text: x, callback_data: x }]
);
const languagesButtons = {
parse_mode: "html",
reply_markup: JSON.stringify({
inline_keyboard: options, // removed [] around option
})
};
In my case InlineKeyboardObject was missing one of the optional parameters
Related
I am using Node.js 16.17 and Express.
Forgive me if this is answered elsewhere, if a solution exists elsewhere, please point me in that direction.
On my server side, I have an array with objects with properties and their values. I want to be able to remove specific text/string from the property values.
What I Have
I currently have an array with objects (and sometimes arrays and object nested within):
DataArray =
[
{
page: {
results: [
{
id: '1234',
title: 'TextIWantA **(Text) I Dont Want**',
children: {
page: {
results: [
{
id: '5678',
title: 'ChildA TextIWant **(Text I) Dont Want**',
},
{
id: '9101',
title: 'ChildB TextIWant **(Text I) Dont Want**',
children: {
page: {
results: [
{
id: 'abcd',
title: 'GrandchildA TextIWant **(Text I (Dont) Want**',
}
]
}
}
},
],
},
},
},
{
id: '1121',
title: 'TextIWantB **(Text) I Dont Want**',
}
]
}
}
]
I am able to flatten this structure with this function:
function flatten(arr) {
const flattened = []
for (const { children, ...element } of arr) {
flattened.push(element)
if (children) {
flattened.push(...flatten(children.page.results))
}
}
return flattened;
}
const flat = [{ page: { results: flatten(DataArray[0].page.results) } }]
console.log(flat[0].page.results)
The returned data is:
[
{ id: '1234', 'page', title: 'TextIWantA **(Text) I Dont Want**' },
{ id: '5678', 'page', title: 'ChildA TextIWant **(Text I) Dont Want**' },
{ id: '9101', 'page', title: 'ChildB TextIWant **(Text I) Dont Want**' },
{ id: 'abcd', 'page', title: 'GrandchildA TextIWant **(Text I (Dont) Want**' },
{ id: '1121', 'page', title: 'TextIWantB **(Text) I Dont Want**' }
]
I am making an assumption that I have to change my text to a string in order to replace it then parse it again to turn back into an object. I'm happy to learn my assumption is true or incorrect, if incorrect, how to fix to be able to remove text.
So if I try to do a replace using the following, 1) it does not work and 2) it does not differentiate for the different text to remove (perhaps I just run multiple/different replaces/filters?):
const veryFlat = flat;
var veryFlatData = veryFlat.map(function(x){return x.toString().replace(/ **(Text) I Dont Want**/g, '');});
var removedTextData= JSON.parse(veryFlatData);
console.log(removedTextData);
Desired Result
I want to be able to remove all of the variances of Text I Dont Want, so the end result would look like (of now it will be flattened as seen above)
DataArray =
[
{
page: {
results: [
{
id: '1234',
title: 'TextIWantA',
children: {
page: {
results: [
{
id: '5678',
title: 'ChildA TextIWant',
},
{
id: '9101',
title: 'ChildB TextIWant',
children: {
page: {
results: [
{
id: 'abcd',
title: 'GrandchildA TextIWant',
}
]
}
}
},
],
},
},
},
{
id: '1121',
title: 'TextIWantB',
}
]
}
}
]
Each title is unique and I don't seem able to find anything to even say I've tried this or that.
I don't want to use .startswith or .length or .index and would prefer to avoid regex, and the example above using .replace doesn't seem to work.
How do I reach into these property values and rip out the text I don't want?
Thank you for any help you can provide.
I have two models which are Licences and Items.
models.Licences.hasMany(models.Items, {
foreignKey: 'licenceId',
onDelete: 'CASCADE',
});
Licence can has many Items.
Items model has licenceId and description.
try {
return await db.Licences.findAll({
order: [['createdAt', 'DESC']],
attributes: [
'id',
'desc',
'version',
[db.Sequelize.col('Items.description'), 'itemDesc']
],
include: [
{
model: db.Items,
attributes: []
},
],
});
} catch (error) {
throw error;
}
}
This method return only one item description. However I want to return response as;
[
{
id: 1,
desc: 'LicenceOne',
itemDesc: [
"exone",
"extwo"
]
}
]
How can I do this?
You can try the below way and make changes.
try {
return await db.Licences.findAll({
order: [['createdAt', 'DESC']],
attributes: [
'id',
'desc',
'version',
],
include: [
{
model: db.Items,
attributes: ['description'],
},
],
});
} catch (error) {
throw error;
}
}
But you will get the below output. Not exactly how you want but you can reprocess all array object and have all description as a array of string in each object. Sequelize will not automatically process the array directly as per what I know.
[
{
id: 1,
desc: 'LicenceOne'
version: '1.1.1',
Items: [
{
description: 'the desctiption 1'
},
{
description: 'the desctiption 2'
},
{
description: 'the desctiption 3'
},
]
},
{
id: 2,
// and on...
}
]
Currently, I'm receiving an API request that has the crazy structure to the data, I'm attempting to parse the XML part of the response to an array/JSON so I can handle it.
Here's the exact request I am receiving:
{
username: 'test',
apiaccesskey: 'aa7a8157-3c17-4b63-806f-7aeff42ae21f',
action: 'placeimeiorder',
requestformat: 'JSON',
parameters:
'<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n',
version: '7.2'
}
I've tried parsing using this library (xml2js) but it's generating a result like this:
let parseresult = await parser.parseStringPromise(req.body.parameters);
console.log(parseresult);
{ PARAMETERS:
{ CUSTOMFIELD: [ 'bnVsbA==' ],
ID: [ '1' ],
SERVICEID: [ '1' ],
IMEI: [ '12345678910' ],
QNT: [ '1' ],
SERVER: [ '0' ],
MODELID: [ '' ],
PROVIDERID: [ '' ],
NETWORK: [ '' ],
PIN: [ '' ],
KBH: [ '' ],
MEP: [ '' ],
PRD: [ '' ],
TYPE: [ '' ],
LOCKS: [ '' ],
REFERENCE: [ '' ],
SN: [ '' ],
SECRO: [ '' ] } }
which is far from ideal when trying to handle, how could I change it so I could simply access individual key/values like parseresult.IMEI or parseresult.CUSTOMFIELD
Should just be a setting.
Code:
const xml2js = require('xml2js');
const parser = new xml2js.Parser({ explicitArray: false });
const xml = "<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n";
parser.parseString(xml, (err, result) => {
console.dir(result);
});
Reference: https://github.com/Leonidas-from-XIV/node-xml2js#options
Output:
{
PARAMETERS: {
CUSTOMFIELD: 'bnVsbA==',
ID: '1',
SERVICEID: '1',
IMEI: '12345678910',
QNT: '1',
SERVER: '0',
MODELID: '',
PROVIDERID: '',
NETWORK: '',
PIN: '',
KBH: '',
MEP: '',
PRD: '',
TYPE: '',
LOCKS: '',
REFERENCE: '',
SN: '',
SECRO: ''
}
}
Alternative:
Using the async/await like you have above:
const xml2js = require('xml2js');
(async () => {
const parser = new xml2js.Parser({ explicitArray: false });
const xml = "<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n";
try {
console.log(await parser.parseStringPromise(xml))
} catch (error) {
console.log('ERROR', error);
}
})();
If you want complete control over the conversion, try saxon-js (released on Node.js a couple of weeks ago, available from npm).
For example you could do it in XPath 3.1 like this:
const SaxonJS = require("saxon-js");
SaxonJS.getResource({text:xml, type:"xml"})
.then(doc => {
SaxonJS.serialize(
SaxonJS.XPath.evaluate(
"map:merge(/PARAMETERS/* ! map{name(): string()})", doc),
{method:"json", indent:true}))});
You could extend this, for example to select which fields of the XML to include in the result.
Or for even more flexibility, you could do it in XSLT3.
(Not tested.)
you can also use camaro, a xpath-powered template to transform the value. it looks like this
const { transform } = require('camaro')
const data = {
username: 'test',
apiaccesskey: 'aa7a8157-3c17-4b63-806f-7aeff42ae21f',
action: 'placeimeiorder',
requestformat: 'JSON',
parameters:
'<PARAMETERS><CUSTOMFIELD>bnVsbA==</CUSTOMFIELD><ID>1</ID><SERVICEID>1</SERVICEID><IMEI>12345678910</IMEI><QNT>1</QNT><SERVER>0</SERVER><MODELID></MODELID><PROVIDERID></PROVIDERID><NETWORK></NETWORK><PIN></PIN><KBH></KBH><MEP></MEP><PRD></PRD><TYPE></TYPE><LOCKS></LOCKS><REFERENCE></REFERENCE><SN></SN><SECRO></SECRO></PARAMETERS>\n',
version: '7.2',
}
async function main() {
console.log(await transform(data.parameters, {
customerField: 'PARAMETERS/CUSTOMFIELD',
customerId: 'PARAMETERS/ID',
serviceId: 'PARAMETERS/SERVICEID',
}));
}
main()
output
{ customerField: 'bnVsbA==', customerId: '1', serviceId: '1' }
If you want more fields, you can just edit the template
Can you help me?
I don't know how to add reactions as like\dislike to any message in my telegram bot with written by Node.JS
The problem is I don't know how to change query.message.message_id because message_id every time is different on other chats.
How to count like\dislike I don't know yet too.
Can someone explain to me how to do it well?
Thanks
bot.on('callback_query', query => {
const chatID = query.message.chat.id
switch (query.data) {
case 'good':
console.log(query)
User.find({}).then(users => {
for(var i=0; i < users.length; i++){
var pars = users[i]
bot.editMessageReplyMarkup({
inline_keyboard: [
[
{
text: '👍1',
callback_data: 'good'
},
{
text: '👎',
callback_data: 'bad'
}
]
]
}, {chat_id: pars.telegramId, message_id: query.message.message_id}).catch(e => console.log(e))
}
})
break
case 'bad':
break
}
})
Here for you: 😉
const helpKeyboard = [[{
text: `back`,
callback_data: `back3`
}]]
bot.on('callback_query', msg => {
if (msg.data == `back3`) {
bot.editMessageText(`?`, {
parse_mode: 'Markdown',
message_id: msg.message.message_id,
chat_id: msg.message.chat.id,
reply_markup: {
inline_keyboard: [[{
text: `🔙`,
callback_data: `?`
}]]
}
})
}
}
I'm working on a Flowchart editor and I want the ui.inspector to edit labels on links.
I did the following:
function createInspector(cellView) {
if (!inspector || inspector.options.cellView !== cellView) {
if (inspector) {
inspector.remove();
}
inspector = new joint.ui.Inspector({
inputs: {
labels:
attrs: {
text:{
text: { type: 'textarea', group: 'Labels', label: 'Label', index: 2 },
}
}
},
},
},
groups: {
labels:[ { label: 'Labels', index: 1 },
}],
cellView: cellView
});
$('#inspector-holder-create').html(inspector.render().el);
}
}
paper.on('cell:pointerdown', function(cellView) {
createInspector(cellView);
});
However, when I edit a link it shows in the JSON output:
"labels": {
"0": {
"attrs": {
"text": {
"text": "Text I entered"
}
}
}
},
but doesn't actually render on the link in the stencil.
I think the problem is with the { "0": part the inspector adds. I want to remove that and replace with it [ ] so the output will be
labels: [
{ attrs: { text: { text: 'label' } } }
]
What should I do ??
It is possible to define Inspector inputs with paths.
'labels/0/attrs/text/text': {
type: 'text',
group: 'Text',
index: 1,
label: 'Label'
}
Or as a combination of attributes nesting and paths.
'labels/0/attrs': {
text: {
text: {
type: 'text',
group: 'Text',
index: 1,
label: 'Label'
},
fontSize: {
type: 'number',
group: 'Text',
index: 2,
label: 'Font Size'
}
}
}
This is valid for Rappid v2.4.0+.
inspector = new joint.ui.Inspector({
inputs: {
'labels': [
{attrs: {
text: {
text: {
type: 'text',
group: 'someGroup',
index: 1,
label: "Label"
}
}
}}
]
}});