Hello People I'm building an irc bot using python twisted, everything is built but the bot doesn't respond to commands like i want it to. For example if i want to call a bot command on the irc channel i want to be able to call it like this $time and have the bot reply what time it is, i am able to get it to work like this -> crazybot $time and it prints the time but i don't want to have to type the name every time...How do i get the the bot to run the commands without calling the name first ?
Here is the update -> everything connects
.......
def privmsg(self, user, channel, msg):
user = user.split('!', 1)[0]
if not msg.startswith('#'): # not a trigger command
return # do nothing
command, sep, rest = msg.lstrip('#').partition(' ')
func = getattr(self, 'command_' + command, None)
def command_time(self, *args):
return time.asctime()
....
When i type !time there is no error and no output ..
You could modify MyFirstIrcBot:
Replace ! by $ in:
if not message.startswith('!'): # not a trigger command
return # do nothing
command, sep, rest = message.lstrip('!').partition(' ')
Add:
from datetime import datetime
# ...
def command_time(self, rest):
return datetime.utcnow().isoformat()
Related
I'm attempting to use the python Twitch-Python library for a simple Twitch bot to connect to chat and respond to messages. The bot works as expected, but about 1/6 times the bot fails to connect and the program simply ends. As explained on this error page, it seems to be because the thread ends and doesn't return anything at all https://github.com/PetterKraabol/Twitch-Python/issues/45
I have moved the Chat class into my own code, and have the following:
def __init__(self, channel: str, nickname: str, oauth: str, helix: Optional['twitch.Helix'] = None):
super().__init__()
self.helix: Optional['twitch.Helix'] = helix
self.irc = tc.IRC(nickname, password=oauth)
self.irc.incoming.subscribe(self._message_handler)
self.irc.start()
self.channel = channel.lstrip('#')
self.joined: bool = False
print(self.irc.is_alive())
The class is called with two different functions elsewhere, like so:
def handle_message(self, message : twitch.chat.Message) -> None:
print(message.sender, message.text)
def main(self):
ircConn = TCH(channel='#CHANNEL NAME", nickname="BOT NAME", oauth="SAMPLEOAUTH")
ircConn.subscribe(self.handle_message)
The issue is that about 1 in 6 times, the subscribe() doesn't actually do anything and the program ends. I'd like to find a way to detect when it fails, so that I may attempt a retry, but nothing I've found works. The function/class doesn't return anything, adding an on_error or on_completed argument to the subscribe() doesn't call anything, and using sys.excepthook also doesn't catch it failing.
Additionally, the IRC thread always prints True for irc.is_alive(), even when it fails afterwards.
How can I catch the thread failing? Thank you.
For example, we have a command called a chart /chart Now I want to get the value that the user enters after this command
example: /chart 123456 now How should i get that 123456?
This is the code used to create the command:
def start(update: Update, context: CallbackContext) -> None:
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
I read the python telegram bot documents but did not find what I was looking for
The code you provided is for when the user input the command /start.
Anyway, you can get the content of the input message directly from the Update and then split the message (because it returns everything that has been written, including the command itself) by the command and obtain the input arguments to the command, like this:
def chart(update: Update, context: CallbackContext) -> None:
"""Send a message with the arguments passed by the user, when the command /chart is issued."""
input_mex = update.message.text
input_args = input_mex.split('/chart ')[1]
update.message.reply_text(input_args)
To make all working correctly, you will have to add a CommandHandler, like this:
updater = Updater(token=TOKEN, use_context=True)
# Get the dispatcher to register handlers
dispatcher = updater.dispatcher
dispatcher.add_handler(CommandHandler('chart', chart))
In this way the output for /chart 12345 will be just 12345, as shown in the picture:
command executed into the telegram chat
to get argument from command line we can use module sys
import sys
print 'Number of arguments:', len(sys.argv), 'arguments.'
print 'Argument List:', str(sys.argv)
$ python test.py arg1 arg2 arg3
Result:
Number of arguments: 4 arguments.
Argument List: ['test.py', 'arg1', 'arg2', 'arg3']
I found this help command from GitHub and put it in my code. I checked all errors and under those errors was the error that config was not defined. How do I fix it?
Link to GitHub:
https://gist.github.com/nonchris/1c7060a14a9d94e7929aa2ef14c41bc2
Code (it's a long code, I know):
import discord
from discord.ext import commands
from discord.errors import Forbidden
"""This custom help command is a perfect replacement for the default one on any Discord Bot written in Discord.py!
However, you must put "bot.remove_command('help')" in your bot, and the command must be in a cog for it to work.
Original concept by Jared Newsom (AKA Jared M.F.)
[Deleted] https://gist.github.com/StudioMFTechnologies/ad41bfd32b2379ccffe90b0e34128b8b
Rewritten and optimized by github.com/nonchris
https://gist.github.com/nonchris/1c7060a14a9d94e7929aa2ef14c41bc2
You need to set three variables to make that cog run.
Have a look at line 51 to 56
"""
async def send_embed(ctx, embed):
"""
Function that handles the sending of embeds
-> Takes context and embed to send
- tries to send embed in channel
- tries to send normal message when that fails
- tries to send embed private with information abot missing permissions
If this all fails: https://youtu.be/dQw4w9WgXcQ
"""
try:
await ctx.send(embed=embed)
except Forbidden:
try:
await ctx.send("Hey, seems like I can't send embeds. Please check my permissions :)")
except Forbidden:
await ctx.author.send(
f"Hey, seems like I can't send any message in {ctx.channel.name} on {ctx.guild.name}\n"
f"May you inform the server team about this issue? :slight_smile: ", embed=embed)
class Help(commands.Cog):
"""
Sends this help message
"""
def __init__(self, bot):
self.bot = bot
#commands.command()
# #commands.bot_has_permissions(add_reactions=True,embed_links=True)
async def help(self, ctx, *input):
"""Shows all modules of that bot"""
# !SET THOSE VARIABLES TO MAKE THE COG FUNCTIONAL!
prefix = # ENTER YOUR PREFIX - loaded from config, as string or how ever you want!
# setting owner name - if you don't wanna be mentioned remove line 49-60 and adjust help text (line 88)
owner = # ENTER YOU DISCORD-ID
owner_name = # ENTER YOUR USERNAME#1234
# checks if cog parameter was given
# if not: sending all modules and commands not associated with a cog
if not input:
# checks if owner is on this server - used to 'tag' owner
try:
owner = ctx.guild.get_member(config.OWNER).mention
except AttributeError as e:
owner = config.OWNER_NAME
# starting to build embed
emb = discord.Embed(title='Commands and modules', color=discord.Color.blue(),
description=f'Use `{prefix}help <module>` to gain more information about that module '
f':smiley:\n')
# iterating trough cogs, gathering descriptions
cogs_desc = ''
for cog in self.bot.cogs:
cogs_desc += f'`{cog}` {self.bot.cogs[cog].__doc__}\n'
# adding 'list' of cogs to embed
emb.add_field(name='Modules', value=cogs_desc, inline=False)
# integrating trough uncategorized commands
commands_desc = ''
for command in self.bot.walk_commands():
# if cog not in a cog
# listing command if cog name is None and command isn't hidden
if not command.cog_name and not command.hidden:
commands_desc += f'{command.name} - {command.help}\n'
# adding those commands to embed
if commands_desc:
emb.add_field(name='Not belonging to a module', value=commands_desc, inline=False)
# setting information about author
emb.add_field(name="About", value=f"The Bots is developed by Chriѕ#0001, based on discord.py.\n\
This version of it is maintained by {owner}\n\
Please visit https://github.com/nonchris/discord-fury to submit ideas or bugs.")
emb.set_footer(text=f"Bot is running {config.VERSION}")
# block called when one cog-name is given
# trying to find matching cog and it's commands
elif len(input) == 1:
# iterating trough cogs
for cog in self.bot.cogs:
# check if cog is the matching one
if cog.lower() == input[0].lower():
# making title - getting description from doc-string below class
emb = discord.Embed(title=f'{cog} - Commands', description=self.bot.cogs[cog].__doc__,
color=discord.Color.green())
# getting commands from cog
for command in self.bot.get_cog(cog).get_commands():
# if cog is not hidden
if not command.hidden:
emb.add_field(name=f"`{config.PREFIX}{command.name}`", value=command.help, inline=False)
# found cog - breaking loop
break
# if input not found
# yes, for-loops have an else statement, it's called when no 'break' was issued
else:
emb = discord.Embed(title="What's that?!",
description=f"I've never heard from a module called `{input[0]}` before :scream:",
color=discord.Color.orange())
# too many cogs requested - only one at a time allowed
elif len(input) > 1:
emb = discord.Embed(title="That's too much.",
description="Please request only one module at once :sweat_smile:",
color=discord.Color.orange())
else:
emb = discord.Embed(title="It's a magical place.",
description="I don't know how you got here. But I didn't see this coming at all.\n"
"Would you please be so kind to report that issue to me on github?\n"
"https://github.com/nonchris/discord-fury/issues\n"
"Thank you! ~Chris",
color=discord.Color.red())
# sending reply embed using our own function defined above
await send_embed(ctx, emb)
def setup(bot):
bot.add_cog(Help(bot))
A config.py file is a file you use to set configurations to your code easily. On the code you sent, it's used to identify a couple of key attributes.
If the original creator hasn't posted the config.py on the github, you can probably do it yourself. First, you will create a .py fle named config, and then you will write on there the attributes that this code calls using config. For example, in one part of the code, we have this:
except AttributeError as e:
owner = config.OWNER_NAME
That means the code is going after the config file to look for the attribute "OWNER_NAME", so, when you create your config.py file on your own folder, you will have to define this attribute, with something like:
OWNER_NAME = "your name or something"
The same goes for every other attribute called through the config.
After you finish defining all of this, make sure the config.py file goes inside the same folder that your code is located, and put on your code this simple line:
import config
And then your code should run.
Is it possible to delete all chat history(messages) of my chat with bot.
So the console version be like:
import os
os.sys("clear") - if Linux
os.sys("cls") - if Windows
All I want is to delete all messages in chat using bot.
def deleteChat(message):
#delete chat code
First of all, if you want to delete history with a bot you should save message ids.
Otherwise, you could use an userbot (using an user account) for clearing it.
You can iter all chat messages and get their ids, and delete them in chunks of 100 messages each iteration.
Warning: itering message history of a chat is not possible with bots and BotAPI, because of Telegram limits. So you should use an MTProto API framework, with an user account as said before.
First of all, pyrogram library is needed for doing this (you could also use telethon), and instance a Client, then you can add an handler or start Client using with keyword. Then get all messages ids by itering the chat, and save them in a list. Finally, delete them using delete_messages Client method:
import time, asyncio
from pyrogram import Client, filters
app = Client(
"filename", # Will create a file named filename.session which will contain userbot "cache"
# You could also change "filename" to ":memory:" for better performance as it will write userbot session in ram
api_id=0, # You can get api_hash and api_id by creating an app on
api_hash="", # my.telegram.org/apps (needed if you use MTProto instead of BotAPI)
)
#app.on_message(filters.me & filters.command("clearchat") & ~filters.private)
async def clearchat(app_, msg):
start = time.time()
async for x in app_.iter_history(msg.chat.id, limit=1, reverse=True):
first = x.message_id
chunk = 98
ids = range(first, msg.message_id)
for _ in (ids[i:i+chunk] for i in range(0, len(ids), chunk)):
try:
asyncio.create_task(app_.delete_messages(msg.chat.id, _))
except:
pass
end = time.time() - start
vel = len(ids) / end
await msg.edit_text(f"{len(ids)} messages were successfully deleted in {end-start}s.\n{round(vel, 2)}mex/s")
app.run()
Once you start the userbot, add it in a group, and send "/clearchat". If the userbot has delete messages permission, it will start deleting all messages.
For pyrogram documentation see https://docs.pyrogram.org.
(however, you should not print all messages in the terminal, to avoid server overloading)
And the right code for clearing the console is this:
import os
def clear():
os.system("cls" if os.name == "nt" else "clear")
as seen in How to clear the interpreter console?.
P.S.
You can use the same code, adding bot_token="" parameter to Client, and deleting iter_history part, for deleting messages with a bot if you have the messages ids.
If in the future you'll want to receive messages from a group and print them, but you don't receive the message update, add the bot as admin in the group or disable bot privacy mode in BotFather.
For better pyrogram performance, you should install tgcrypto library.
Alright guys, so here's my problem.
I am in the process of developing a Slack app with a packaged bot that allows the user to play a game in Slack. I have successfully built the bot and packaged it with the app as per the API guidelines. Once I discovered the Interactive Messages feature, I decided to implement said feature for a more user-friendly take on the game.
The interactive messages feature allows you to post messages with buttons, which the user can click to invoke an action. My bot script, let's call it bot.py, prompts the user (using the Slack chat.postMessage function) with a message containing some buttons from which to choose. This script has one class (I know it should be more modular but all in good time), which opens a web-socket for communication via the Slack RTM API. As such, when the script runs, it is always "listening" for a command from a user in a channel directed as follows: #botname command. The portion of the script that invokes this "always listening" state looks like this:
#bot.py
...
if slack_client.rtm_connect():
print("MYBOT v1.0 connected and running!")
while True:
command, channel, user = self.parse_slack_output(slack_client.rtm_read())
if command and channel:
if channel not in self.channel_ids_to_name.keys():
#this (most likely) means that this channel is a PM with the bot
self.handle_private_message(command, user)
else:
self.handle_command(command, channel, user)
time.sleep(READ_WEBSOCKET_DELAY)
else:
print("Connection failed. Invalid Slack token or bot ID?")
That's all good and fine. Now, let's say the user has used a command to successfully create a game instance and has started playing. At a certain point, the user is prompted for a trump suit like so:
#bot.py
...
attachments =[{
"title":"Please select index for trump suit:",
"fallback":"Your interface does not support interactive messages.",
"callback_id":"prompt_trump_suit",
"attachment_type":"default",
"actions":
[{"name":"diamonds","text":":diamonds:","type":"button","value":"0"},
{"name":"clubs","text":":clubs:","type":"button","value":"1"},
{"name":"hearts","text":":hearts:","type":"button","value":"2"},
{"name":"spades","text":":spades:","type":"button","value":"3"}]
}]
slack.chat.post_message(
channel=player_id,
as_user=True,
attachments=attachments
)
The interactive message looks like this. The action of clicking on one of the buttons in this message sends a payload via an HTTP POST to a web server. My other script in the project, which we will call app.py, is a Flask script which successfully receives this POST request when the user clicks one of the buttons. The portion of the script that receives the POST request looks like this:
#app.py
...
# handles interactive button responses for mybot
#app.route('/actions', methods=['POST'])
def inbound():
payload = request.form.get('payload')
data = json.loads(payload)
token = data['token']
if token == SLACK_VERIFICATION_TOKEN:
print 'TOKEN is good!'
response_url = data['response_url']
channel_info = data['channel']
channel_id = channel_info['id']
user_info = data['user']
user_id = user_info['id']
user_name = user_info['name']
actions = data['actions'][0]
value = actions['value']
print 'User sending message: ',user_name
print "Value received: ",value
return Response(), 200
When the button is clicked, I get the expected output:
TOKEN is good!
User sending message: my_username
Value received: 3
So everything is successful up to this point. Now, what I want to do is to take that POST information and use it to invoke a function in my bot.py script that handles the trump suit selection. The problem is that if I were to invoke that function, let's call it handle_trump_suit_selection(), I would first have to instantiate a Bot() object in the app.py file, which of course would not work as desired because the function would be called with a new Bot() instance, and therefore would not be in the same state as the current game.
So how the heck can I get the POST information back to the desired Bot() instance in bot.py for further processing? I'm new to OOP in Python and especially new to Flask and the Slack API, so go easy on me ;).
Thanks in advance.
Great Success!
tl;dr:
Basically, the solution was to create a Celery task that instantiated the bot instance from the Flask app using the Slack Events API. You set the task to start after the desired input has been entered, promptly return the required Response(200) back to Slack, while in the meantime the bot script (which starts up the RTM API web-socket) launches in parallel.
The nitty gritty:
So, as stated above, it turns out that what was required was a queuing service of some sort. I ended up going with Celery for its relative ease at integrating with Heroku (where I host the Slack app) and its easy-to-follow documentation.
Developing your Slack app this way requires setting up and using the Slack Events API to receive the command ("play my_game" in this example) from the Slack channel the message was posted in. The Flask app (app.py) portion of the program listens for this event, and when the input matches up with what you're looking for, it launches the Celery task in parallel (in tasks.py, which instantiates a Bot() instance of bot.py in this example). :) Now the bot can listen and respond using both the Slack RTM API and the Slack Events API. This allows you to build rich applications/services within the Slack framework.
If you are looking to set up something similar, below are my project layout and the important code details. Feel free to use them as a template.
Project Layout:
project_name_folder
app_folder
static_folder
templates_folder
__init__.py
my_app.py
bot.py
tasks.py
Procfile
requirements.txt
__init__.py:
from celery import Celery
app = Celery('tasks')
import os
app.conf.update(BROKER_URL=os.environ['RABBITMQ_BIGWIG_URL']) # Heroku Celery broker
my_app.py:
from flask import Flask, request, Response, render_template
import app
from app import tasks
app = Flask(__name__)
#app.route('/events', methods=['POST'])
def events():
"""
Handles the inbound event of a post to the main Slack channel
"""
data = json.loads(request.data)
try:
for k, v in data['event'].iteritems():
ts = data['event']['ts']
channel = data['event']['channel']
user_id = data['event']['user']
team_id = data['team_id']
if 'play my_game' in str(v):
tasks.launch_bot.delay(user_id, channel, ts, team_id) # launch the bot in parallel
return Response(), 200
except Exception as e:
raise
bot.py:
from slackclient import SlackClient
class Bot():
def main():
# opening the Slack web-socket connection
READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
if self.slack_client.rtm_connect():
while True:
command, channel, user, ts = self.parse_slack_output()
if command and channel:
if channel not in self.channel_ids_to_name.keys():
# this (most likely) means that this channel is a PM with the bot
self.handle_private_message(command, user, ts)
else:
self.handle_command(command, channel, user, ts)
time.sleep(READ_WEBSOCKET_DELAY)
tasks.py:
import bot
from bot import Bot
from app import app
#app.task
def launch_bot(user_id, channel, ts, team_id):
'''
Instantiates the necessary objects to play a game
Args:
[user_id] (str) The id of the user from which the command was sent
[channel] (str) The channel the command was posted in
[ts] (str) The timestamp of the command
'''
print "launch_bot(user_id,channel)"
app.control.purge()
bot = Bot()
bot.initialize(user_id, channel)
bot.main()
Procfile (if using Heroku):
web: gunicorn --pythonpath app my_app:app
worker: celery -A app.tasks worker -B --loglevel=DEBUG
Please let me know if you are having any issues. This took me a little while to figure out, and I would be happy to help you if you are banging your head on this one.