How do I have my Bot respond with arguments? - telegram

So I've built a Telegram bot, which can receive the following commands:
/list
/info 123
This works great, as I can catch /info and pass the additional arguments as ints. But, sadly, the Telegram clients don't see /info 123 as a complete command, but just the /info part. Is there a way to make it recognize the entirety of the command as the command?
I've tried Markdown-ing it: [/info 123](/info 123), but no joy. Is this possible?

I've reached out to #BotSupport with the same question, and he/they/it responded swiftly with the following answer:
Hi, at the moment it is not possible to highlight parameters of a command. I any case, you may can find a workaround if you use correct custom keyboards ;)
— #BotSupport
Custom keyboards may be an option for someone, but not for me. The solution I've gone for is to give the command as /info123. As the bot receives all / commands, I check if the received command starts with info, and if so, I remove the info part. I convert the remaining string/int to arguments, and pass that along to the relevant command.

If you mean to pass the 123 as an argument for your command info and if you happen to use the python-telegram-bot, then here's how you do it:
dispatcher.add_handler(CommandHandler('hello', SayHello, pass_args=True))
According to the documentation: pass_args Determines whether the handler should be passed the arguments passed to the command as a keyword argument called args. It will contain a list of strings, which is the text following the command split on single or consecutive whitespace characters. Default is False.

you can use RegexHandler() to do this.
Here is an example
def info(bot, update):
id = update.message.text.replace('/info_', '')
update.message.reply_text(id, parse_mode='Markdown')
def main():
updater = Updater(TOKEN)
updater.dispatcher.add_handler(RegexHandler('^(/info_[\d]+)$', info))
updater.start_polling()
Usage
The command /info_120 will return 120
and /info_007 will return 007
UPDATE
for newer versions, you may use this method instead!
MessageHandler(filters.Regex(r'^(/info_[\d]+)$'), info)

To get the argument of command you don't even need to use pass_args as said Moein you can simply get it from context.args look at Github page. So you can pass as many arguments as you want and you will get a list of arguments! Here is an example from Github.
def start_callback(update, context):
user_says = " ".join(context.args)
update.message.reply_text("You said: " + user_says)
...
dispatcher.add_handler(CommandHandler("start", start_callback))

ForceReply
Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode.
a simple shot
In this case, a user should send a valid number with /audio command (e.g. /audio 3, if they forgot it, we can inform and force them to do so.
source:
https://core.telegram.org/bots/api#forcereply

This is a fairly rudimentary way of creating kwargs from user input.
Unfortunately, it does require the user to be aware of the fields that can be used as parameters, but if you can provide informative response when the user doesnt provide any detectable kwarg style messages then you could probably make a better experience.
As I say, extremely rudimentary idea, and would probably be achieved faster with the regex filters available. And this would be much more reliable when checking input from the user of the "pesky" variety.
The script relies on || delimiter preceeding the command and as is shown will trim any extra characters like new lines and spaces
You can remove the extra check for commit as this is provided in order to tell the bot that you want to save your input to the database explicitly.
def parse_kwargs(update):
commit = False
kwargs = {}
if update.message:
for args in update.message.text.split('||')[1:]:
for kw_pair in args.split(','):
key, value = kw_pair.split('=')
if key.strip() != 'commit':
kwargs[key.strip()] = value.strip()
elif key.strip() == 'commit' and value.strip().lower() == 'true':
commit = True
return kwargs, commit

Related

enforce-guard fails in Zelcore and XWallet

Whenever I call a function that has (enforce-guard some-guard) from X-wallet or Zelcore it always fails with the error Keyset failure (keys-all)
I have no issues when doing this from Chainweaver
How to fix this
This is an issue if you are also providing capabilities with your request.
To fix this, you will need to put enforce-guard within a capability too.
So you will need to do something like
(defcap VERIFY_GUARD (some-guard:guard)
(enforce-guard some-guard)
)
And wherever you would call enforce-guard , you will then need to do
(with-capability (VERIFY_GAURD some-guard)
; Guarded code here
)
Why does this happen?
Chainweaver allows you to select unrestricted signing keys, which provides a key/guard for enforce-guard to work with.
However X-Wallet and Zelcore don't provide this if capabilities present on the request (otherwise they do).
It is probably better practice to add enforce-guard into capabilities anyways and use require-capability in places where you expect the guard to pass.

context.job_queue.run_once not working in Python Telegram BOT API

I'm trying to setup a bot which:
Receives the keywords in /search_msgs userkey command from a TG group
Search in DB for that userkey and send back appropriate text back
I'm getting two errors
None type object has no attribute args, in callback_search_msgs(context), see code snippet
AttributeError: 'int' object has no attribute 'job_queue', in search_msgs(update, context), see code snippet.
Telegram's official documents is way too difficult for me to read and understand. Couldn't find even one place where Updater, update, Commandhandler, context are all explained together with examples.
How to fix this code?
import telegram
from telegram.ext import Updater,CommandHandler, JobQueue
token = "Token"
bot = telegram.Bot(token=token)
# Search specific msgs on user request
def search_msgs(update, context):
context.job_queue.run_once(callback_search_msgs, context=update.message.chat_id)
def callback_search_msgs(context):
print('In TG, args', context.args)
chat_id = context.job.context
search_msgs(context, chat_id)
def main():
updater = Updater(token, use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("search_msgs",search_msgs, pass_job_queue=True,
pass_user_data=True))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
Let me first try & clear something up:
Telegram's official documents is way too difficult for me to read and understand. Couldn't find even one place where Updater, update, Commandhandler, context are all explained together with examples.
I'm guessing that by "Telegram's official documents" you mean the docs at https://core.telegram.org/bots/api. However, Updater, CommandHandler and context are concepts of python-telegram-bot, which is one (of many) python libraries that provides a wrapper for the bot api. python-telegram-bot provides a tutorial, examples, a wiki where a lots of the features are explained and documentation.
Now to your code:
In context.job_queue.run_once(callback_search_msgs, context=update.message.chat_id) you're not telling job_queue when to run the the job. You must pass an integer or a datetime.(date)time object as second argument.
in
def callback_search_msgs(context):
print('In TG, args', context.args)
chat_id = context.job.context
search_msgs(context, chat_id)
you are passing context and chat_id to search_msgs. However, that function treats context as an instance of telegram.ext.CallbackContext, while you pass an integer instead. Also, even if that worked, this would just schedule another job in an infinite loop.
Finally, I don't understand what scheduling jobs has to do with looking up a key in a database. All you have to do for that is something like
def search_msgs(update, context):
userkey = context.args[0]
result = look_up_key_in_db(userkey)
# this assumes that result is a string:
update.effective_message.reply_text(result)
To understand context.args better, have a look at this wiki page.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

Discord Bot python 3.6 report command

I am making a custom discord bot in python. I am trying to add a !report command.
I am very confused and cant find the answer anywhere. Can anyone help me make it?
I want any user to be able to do !report #example reason.
and save it in a database such as excel or sql3 or preferably in a staff channel.
how would I do this?
I have tried to use on_message()
You could use the on_message() command:
#client.event
async def on_message(message):
if message.content.startswith("!report"):
report_channel = client.get_channel(channel id)
member = text.split(" ")[1]
reason = ' '.join(text.split(" ")[1:])
await report_channel.send(f"Member: {member}, Reason: {reason}")
So the first thing is to look to see if the person used the "!report" command with an if statement.
Next, you find the member by taking the second word of the message.
After that, you find the reason by taking the rest of the words in the message.
Then you send it to the pre-defined report channel on discord.

How to fix "NameError name 'changePlaying' is not defined

I'm using cogs to shorten and organise my discord bot. However upon attempting a "Events" cog I'm faced with a NameError of changePlaying not being defined despite it literally being about the on_ready command
A: I forgot to import discord.ext and hence imported that.
B: I've tried changing the location of the list of possible statuses in and out of the changePlaying event
PlayingList = [Maximus.py.","!help"]
async def changePlaying(self):
while True:
await self.bot.change_presence(game=Game(name=random.choice(PlayingList)))
await asyncio.sleep(120)
async def on_ready(self):
print('Logged in as')
print(self.bot.user.name)
print(self.bot.user.id)
print('-----------------------------------------')
print('Log in complete')
for x in range(5):
print("")
self.bot.loop.create_task(changePlaying(self))
Well I think it's clear what the expected results are but to clarify the bot is supposed to boot. It does come online and does listen to commands but the status bar does not change
In view of the self argument of the methods, I see they are in the cog. You should use PlayingList as an attribute of the cog, that is, in its __init__ add instead self.PlayingList = ["Maximus.py.","!help"] and then access it through self. in the methods.
And so the answer is : you aren't using methods correctly. You have to do self.changePlaying() not changePlaying(self).
By the way, use a tuple instead of a list if you do not plan to modify it through the execution. And a variable name shouldn't start with a capital letter, as it is commonly reserved to classes. See PEP 8.

How am I sure that my process has a user interface?

When running a class which can be used interactively or silently by batch, I want to display a hourglass, only if in interactive mode.
I found the function xGlobal::clientKind() , read below, but not sure it is sufficient (can't batches also run on Client ?)
if (xGlobal::clientKind() == ClientType::Client)
startLengthyOperation();
// here do the process
if (xGlobal::clientKind() == ClientType::Client)
endLengthyOperation();
Do not bother to test client kind when using startLengthyOperation, the method does a sufficient test itself.
Testing should be like this:
if (clientKind() == ClientType::Client)
...
Don't use xGlobal::clientKind, use without qualification.
The ClientType has four values, matching what you see in "Online Users".
Batch can be called interactively in Basic/Periodic/Batch, but it should be rarely used.

Resources