How to apply { parse_mode: 'Markdown' } for reply with InlineKeyboardButton ?
const Telegraf = require("telegraf");
const Extra = require("telegraf/extra");
const Markup = require("telegraf/markup");
const keyboard = Markup.inlineKeyboard([
Markup.urlButton("❤️", "http://telegraf.js.org"),
Markup.callbackButton("Delete", "delete")
]);
const myReply = "Hello *mate*, __where are you ?__"
bot.on("message", ctx =>
ctx.telegram.sendMessage(ctx.chat.id, myReply, Extra.markup(keyboard))
);
Is there any option to add markdown style for message with InlineKeyboardButton ?
Telegraf 4
According to Telegraf 4.0 changelog, Extra is removed entirely.
If you have an inline-keyboard in a simple ctx.reply() (see the example), just use .replyWithHTML() or .replyWithMarkdown() or .replyWithMarkdownV2() instead of .reply().
ctx.replyWithMarkdownV2(
'*formatted* text',
Markup.inlineKeyboard([
Markup.button.callback('Coke', 'Coke'),
Markup.button.callback('Dr Pepper', 'Dr Pepper'),
Markup.button.callback('Pepsi', 'Pepsi')
])
)
If you have a more complex situation, you can pass an object like below as the appropriate argument to the ctx.reply() or ctx.telegram.sendMessage() or bot.telegram.editMessageText() etc.:
const extraObject = {
parse_mode: 'HTML',
...Markup.inlineKeyboard([
Markup.button.callback('Coke', 'Coke'),
Markup.button.callback('Pepsi', 'Pepsi'),
]),
}
ctx.telegram.sendMessage(ctx.chat.id, 'My <b>formatted</b> reply', extraObject)
To add Markdown styling to your message, chain markdown() to Extra.markup(), like this:
const keyboard = Markup.inlineKeyboard([
Markup.urlButton("❤️", "http://telegraf.js.org"),
Markup.callbackButton("Delete", "delete")
]);
const myReply = "Hello *mate*, _where are you ?_"
bot.on("message", ctx => {
ctx.telegram.sendMessage(ctx.chat.id, myReply, Extra.markdown().markup(keyboard));
});
Related
I want to make a fallback for a share button.
If useShareDialog isSupported = open ShareDialog
If useShareDialog !isSupported = copy to Clipboard
If useClipboard !isSupported = don't show the share button
VueUse has the useShare and useClipboard functions. But both use the same variable to check if the function is supported with the isSupported variable
How I can separate them?
Vue Component
<script setup>
import {
useClipboard, useShare
} from '#vueuse/core'
const { share, isSupported } = useShare({
title: 'Marcus Universe Portfolio',
text: 'Look at my awesome portfolio',
url: 'https://marcus-universe.de',
})
const {copy, copied, isSupported} = useClipboard({
source: 'https://marcus-universe.de'
})
function shareTrigger() {
if(isSupported) {
share()
} else {
copy()
}
}
</script>
<template>
<button
class="shareButtons"
#click="shareTrigger()"
v-if="isSupported">
{{ copied ? 'copied to clipboard' : 'Share' }}
</button>
</template>
You can rename variable:
const { share, isSupported: isSupportedShare } = useShare({...})
const {copy, copied, isSupported: isSupportedClipboard} = useClipboard({...})
This is an example of my database
I am making a website using nodejs, MongoDB, and pug. I have displayed this data in a pug form with the data in the select boxes. Like this but in pug using for loops and all the data-
<!DOCTYPE html>
<html>
<body>
<form>
<select name="Type">
<option value="Electronic">Electronic</option>
</select>
<select name="Brands">
<option value="Apple">Apple</option>
<option value="Dell">Dell</option>
<option value="Samsung">Samsung</option>
</select>
<select name="Products">
<option value="Macbook Pro">Macbook Pro</option>
<option value="Inspioren">Inspioren</option>
<option value="Galaxy S20">Galaxy S20</option>
</select>
</form>
</body>
</html>
What I want to do is, change the select box options based on what the user has selected in other select options.
For example:- If a user selects "Type: "Electronic", and "Brand: Apple",I want to display only Apple products as the options.
Or if a user selects "Type: "Electronic" it should automaticly display brands list in the select box for brands.
The data will be added from the database (it will not be hardcodded).
One possibility could be to modify your Pug view to include select tag id's and leave the downstream selects blank and then add a script at the end of your view to add event listeners and do some DOM manipulation using JQuery or just javascript after the view is loaded. The event handlers would then allow you to conditionally fill in your next select tag based on whatever you choose in the current tag.
Something along the lines of:
script.
document.addEventListener("DOMContentLoaded",
function(e) {
const typeSelect = document.getElementById('type')
typeSelect.onchange = function(e) {
const brandSelect = document.getElementById('brand')
if (typeSelect.options[typeSelect.selectedIndex].text === 'Apple') {
['MacBook Pro', 'MacBook Air', 'MacBook Mini'].forEach(function(product) {
let newOption = createElement('option')
newOption.textContent = product
brandSelect.appendChild(newOption
}
else if ( // etc... ) {}
else {
// functionality to clear out the downstream select tags.
}
}
const brandSelect = document.getElementById('brand')
brandSelect.onchange = // etc, etc....
})
Server side code, with some test data:
app.get("/avi", (req, res) => {
res.render("avi", { Types: [ "Electronic"], data:
[ { Type: "Electronic", Brand: "Apple", Products: [ "MacBook Pro", "MacBook Air", "Mac Mini"]},
{ Type: "Electronic", Brand: "Dell", Products: [ "Inspioren", "Latitude"]},
{ Type: "Electronic", Brand: "Samsung", Products: [ "Galaxy S20", "Galaxy S10"]}
]
});
});
You should include a Types list, otherwise your Pug script will have to go through the raw data to identify the number of unique Types.
avi.pug:
html
head
body
p Linked Dropdowns
form
p Type:
select#types(onchange="typeChanged()")
option(value="") Select Type
each item of Types
option(value=item)=item
p Brand:
select#brands(onchange="brandChanged()")
option(value="") Select Brand
p Product:
select#products()
option(value="") Select Product
script.
const data = JSON.parse(`!{JSON.stringify(data)}`);
const types = document.getElementById("types")
const brands = document.getElementById("brands")
const products = document.getElementById("products")
const typeChanged = () => {
clearBrands(); clearProducts();
if (types.selectedIndex!==0) {
const selectedType = types.options[types.selectedIndex].text;
data.forEach(d => {
if (d.Type===selectedType) {
const newBrand = document.createElement("option")
newBrand.value = newBrand.innerText = d.Brand;
brands.appendChild(newBrand);
}
});
}
}
const brandChanged = () => {
clearProducts();
if (brands.selectedIndex!==0) {
const selectedType = types.options[types.selectedIndex].text;
const selectedBrand = brands.options[brands.selectedIndex].text;
data.forEach(d => {
if (d.Type===selectedType && d.Brand===selectedBrand) {
d.Products.forEach(p => {
const newProd = document.createElement("option")
newProd.value = newProd.innerText = p;
products.appendChild(newProd);
});
}
})
}
}
const clearBrands = () => {
const item0 = brands.childNodes[0];
brands.textContent = "";
brands.appendChild(item0);
}
const clearProducts = () => {
const item0 = products.childNodes[0];
products.textContent = "";
products.appendChild(item0);
}
The data at the server is injected into the client side Javascript with:
const data = JSON.parse(`!{JSON.stringify(data)}`);
This method is not very robust. There will be problems if anywhere in the data there is a backtick ` character.
I am answering my own question here. What I did was add the onchange function on the Type select box. And created a script inside my file, with the following code-
function change() {
var Type_name = document.getElementById("type").value;
var Brand= document.getElementById('brands');
var Product = document.getElementById('products');
Brand.options.length = 0;
Product.options.length = 0;
var DefaultBrand = new Option("Brand", "Null");
var DefaultProduct = new Option ("Product", "Null");
Brand.add(DefaultBrand, undefined);
Product.add(DefaultProduct, undefined);
var product_master = !{JSON.stringify(product_names)};
for (const brands of product_master) {
if (brands.Type == Type_name){
const BrandOption = new Option(brands.Brand, brands.Brand);
Brand.add(BrandOption, undefined);
}
}
And another function similar to this to add products.
This worked for me. But if anyone wants to improve this code, I would appreciate the help.
Here is my html,
<p id="on-board" class="on-board-text">
{{
'OVERVIEW.WELCOME_SCREEN_ON_BOARD'
| translate
: {
USER_NAME:
username
}
}}</p>
Where,
username = 'banner works!';
I am trying to fetch the value of USER_NAME.
Here is my spec,
it('Check if username is populated', async () => {
const { find } = await shallow.render({});
const htmlDiv = find('.on-board-text');
const user = htmlDiv.nativeElement.textContent.trim();
expect(user).toEqual('banner works!');
});
But here I am getting 'user' in spec as empty (" ").
Can anyone please suggest me help.Thanks.
I'm trying to use (https://www.npmjs.com/package/multi-clamp)[multi-clamp.js] to clamp/truncate my titles that are being returned from the reddit api.
However, it is only working on the 1st retuned title rather than all of the titles. How would I ensure that the function runs on all of the titles returned from the api and not just the 1st?
const reddit = "https://www.reddit.com/r/upliftingnews.json?raw_json=1&limit=10"
async function getData() {
try {
let response = await fetch(reddit);
let data = await response.json()
return data;
} catch (error) {
console.log("error", error);
}
}
getData()
.then(data => data.data.children)
.then(data => data.map(post => ({
source: post.data.domain,
author: post.data.author,
link: post.data.url,
img: (typeof (post.data.preview) !== 'undefined') ? post.data.preview.images[0].source.url : null,
title: post.data.title,
})))
.then(data => data.map(render))
const app = document.querySelector('#app');
const render = post => {
//console.log(post.data);
const node = document.createElement('div');
node.classList.add('news-item', `news-item--${ post.class }`);
node.innerHTML = `
<a class="news-item-link" href="${post.link}">
<div style="background-image: url('${post.img}')" class="news-item-bg"></div>
<div class="news-item-content">
<h3 class="news-item-source">${post.source}</h3>
<h2 class="news-item-title mb-2">${post.title}</h2>
</div>
</a>`;
app.appendChild(node);
new MultiClamp(document.querySelector('.news-item-title'), {
ellipsis: '...',
clamp: 2
});
}
new MultiClamp.. is where the clamping runs on the title selector but it's only clamping the first returned title, rather than all of them.
How do I get it to run on all titles?
document.querySelector returns only the first element that matches the selector. Since you are executing it on the whole document, it will always get the first title that you appended to the document, and create many new MultiClamps on it.
Instead, you need to select the one new title element in your new node:
function render(post) {
const node = document.createElement('div');
node.classList.add('news-item', `news-item--${ post.class }`);
node.innerHTML = `
<a class="news-item-link" href="${post.link}">
<div style="background-image: url('${post.img}')" class="news-item-bg"></div>
<div class="news-item-content">
<h3 class="news-item-source">${post.source}</h3>
<h2 class="news-item-title mb-2">${post.title}</h2>
</div>
</a>`;
app.appendChild(node);
new MultiClamp(node.querySelector('.news-item-title'), {
// ^^^^
ellipsis: '...',
clamp: 2
});
}
Btw, using innerHTML with unescaped interpolation values opens up your application for XSS attacks. Better build the entire node contents using the DOM API as well.
Trying to use this package: https://www.npmjs.com/package/passport-ldapauth
I managed to find the ldap-settings for my company in a medawikiserver (php)
$wgLDAPDomainNames = array("COMPANY");
$wgLDAPGroupBaseDNs = array("COMPANY"=>"dc=company,dc=se");
$wgLDAPAutoAuthDomain = "COMPANY";
$wgLDAPGroupUseFullDN = array("COMPANY"=>true );
$wgLDAPServerNames = array("COMPANY"=>"dcst.company.se");
$wgLDAPSearchStrings = array("COMPANY" => "COMPANY\\USER-NAME" );
$wgLDAPSearchAttributes = array("COMPANY"=>"sAMAccountName");
$wgLDAPBaseDNs = array("COMPANY"=>"dc=company,dc=se");
$wgLDAPEncryptionType = array("COMPANY" => "ssl" );
$wgMinimalPasswordLength = 1;
I need to map this to the node-package. I tried this:
var opts = {
server: {
url: 'ldaps://dcst.company.se',
bindDn: 'dc=company,dc=se',
//bindCredentials: 'secret',
searchBase: 'dc=company,dc=se',
searchFilter: '(&(objectcategory=person)(objectclass=user)(|(samaccountname={{username}})(mail={{username}})))',
searchAttributes: ['displayName', 'mail'],
}
};
I get "Bad request". This is from the docs:
badRequestMessage flash message for missing username/password (default: 'Missing credentials')
What have I done wrong?
You need to add the admin credentials. Here's how my configuration works:
var Strategy = require('passport-ldapauth').Strategy
, passport = require('passport')
, config = require('config')
, userLookup = require('./userLookup');
var ldapConfig = {
server: {
url: config.get('ldap.url'),
adminDn: config.get('ldap.adminDn'),
adminPassword: config.get('ldap.adminPassword'),
searchBase: config.get('ldap.searchBase'),
searchFilter: config.get('ldap.searchFilter')
}
};
passport.use('ldap', new Strategy(ldapConfig, userLookup));