How I implement reset_cooldown in a cog file? - python-3.x

I m trying to do a command with which my friends can troll each other by moving them to a special channel.
I put a cooldown so they won t abuse it but know, when i m trying to rewrite my code to a cog version i can t handle the #command.after_invoke so i can t reset cooldown if the author is me or someone special.
I tried to do it in #command.error but if i reset it there i can t reactivate it
My problem is that i can t figure out how am i suppose to transpose this into a cog version:
#m.after_invoke
async def reset_cooldown(ctx):
if ctx.message.author.id==1234567890:
m.reset_cooldown(ctx)
I ll put cog file with my function, it works but thw down is that i can t reset cooldown:
import discord
from datetime import datetime
protected=[1234567890]
class move(commands.Cog):
def __init__(self,bot):
self.bot=bot
#commands.command(pass_context=True,aliases=['m'])
#commands.cooldown(1,300,commands.BucketType.user)
async def move(self,ctx,member:discord.Member):
global protected
message=ctx.message
delete_module=self.bot.get_cog('delete') #this will erase the message so the victim won tknow who s doing teh prank
await delete_module.delete(ctx,ctx.author)
canal_sclav=list(filter(lambda x: x.name=='sclav',ctx.guild.voice_channels))[0] # this is the channel where they llbe sent
reports=list(filter(lambda x: x.id==1234567890,member.guild.channels))[0] # this is a channel in which i track the activity
current_time = datetime.now().strftime("%H:%M:%S")
embed=discord.Embed(description=message.author.mention+'l-a maltrafoxat pe '+member.mention+ ' la ora '+current_time,color=discord.Colour.red()) # this is the sign that someone was abused
######################## daca autorul nu e conectat -- if the author is not connected
if ctx.author.voice==None:
embed=discord.Embed(description=message.author.mention+'Conecteza-te fiti-ar alifia de ras',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca beleste pe cine nu trebuie -- this is for the protected one
elif member.id in protected:
embed=discord.Embed(description=message.author.mention+' altadata, suge-o acum',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca membrul nu e conectat -- if member is not connected
elif member.voice==None:
embed=discord.Embed(description=message.author.mention+' nu poate fi babardit incearca alt fraier',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca membrul e pe sclav -- if he is abused
elif member.voice.channel==canal_sclav:
embed=discord.Embed(description=message.author.mention+' si-o ia in cur schimba prostu',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca e ok -- if it is ok
else:
await reports.send(embed=embed)
await member.move_to(canal_sclav)
embed=discord.Embed(description=member.mention+' este abuzat',color=discord.Colour.gold())
await ctx.send(embed=embed)
# for error handling
#move.error
async def move_error(self,ctx,error):
global protected
retry_after = round(error.retry_after)
text=ctx.message.author.mention+' ti-o pot suge in '+str(round(error.retry_after))+'s'
embed=discord.Embed(description=text,color=discord.Colour.magenta())
await ctx.send(embed=embed)
return await ctx.send(embed=embed
)
# f"\N{HOURGLASS} Command is on cooldown, try again after {retry_after} seconds"
# HERE I TRIED TO DO IT BUT I CAN T FIND ENOUGH RESOURCES
#move.afetr_invoke
async def reset_cooldown(ctx):
if ctx.message.author.id in protected:
move.reset_cooldown(ctx)
def setup(bot):
bot.add_cog(move(bot))

I finally found the answer; the reset function that should be add is this:
async def cog_after_invoke(self,ctx):
if ctx.message.author.id in protejati:
ctx.command.reset_cooldown(ctx)

Related

Discord.PY Help Command

Ok, so I'm having an issue with my bot. I know what it is, but I don't know what the string to fix it is.
Basically I have it set to store config and prefixes and such in a json but the help command which is loading one of my commands multiple times.
I've checked the help command and i don't think that's it. Because after redoing my setprefix command (which is being displayed multiple times) it added yet another one on so now it's even worse.
Here is my storage system that I believe to be causing the issue.
with open("./config/config.json", "r") as configjsonFile:
configData = json.load(configjsonFile)
login_token = configData["discordtoken"]
with open("./config/prefixes.json") as f:
prefixes = json.load(f)
default_prefix = "-"
def prefix(client, message):
id = message.guild.id
return prefixes.get(id, default_prefix)
client = commands.Bot(command_prefix=prefix)
client.remove_command('help')
#client.command(name="setprefix", aliases=["Prefix", "prefix"])
#commands.has_permissions(administrator=True)
async def setprefix(ctx, new_prefix):
"""Change the server prefix for the bot.
Alt : Prefix, prefix
Usage : prefix <custom prefix>"""
prefixes[ctx.message.guild.id] = new_prefix
with open("./config/prefixes.json", "w") as f:
json.dump(prefixes, f, indent=4)
embed = discord.Embed(color=0x4a3d9a, timestamp=ctx.message.created_at)
embed.set_author(name=f"{client.user.name}", icon_url=client.user.avatar_url)
embed.add_field(name="Success", value=f"Successfully changed prefix changed to `{new_prefix}`")
embed.set_thumbnail(url=client.user.avatar_url)
embed.set_footer(text="NewHorizon Development | https://newhorizon-development.netlify.app", icon_url=client.user.avatar_url)
await ctx.message.delete()
await ctx.send(embed=embed, delete_after=4)
and here is my custom help command just in case I'm wrong about what is causing the issue.
#commands.command(name="help", aliases=["Help", "H", "h"])
#commands.has_permissions(add_reactions=True, embed_links=True)
async def help(self, ctx, *cog):
"""Gets all cogs and commands.
Alt : h, H, Help
Usage : [h]elp <cog or command>"""
try:
if not cog:
"""Cog listing. What more?"""
embed = discord.Embed(title='Cog Listing and Uncatergorized Commands',
description='Use `help <cog>` to find out more about them!\n(BTW, the Cog Name Must Be in Title Case, Just Like this Sentence.)')
cogs_desc = ''
for x in self.client.cogs:
cogs_desc += ('{} - {}'.format(x, self.client.cogs[x].__doc__) + '\n')
embed.add_field(name='Cogs', value=cogs_desc[0:len(cogs_desc) - 1], inline=False)
cmds_desc = ''
for y in self.client.walk_commands():
if not y.cog_name and not y.hidden:
cmds_desc += ('{} - {}'.format(y.name, y.help) + '\n')
embed.add_field(name='Uncatergorized Commands', value=cmds_desc[0:len(cmds_desc) - 1], inline=False)
await ctx.message.add_reaction(emoji='✉')
await ctx.message.author.send('', embed=embed)
else:
"""Helps me remind you if you pass too many args."""
if len(cog) > 1:
embed = discord.Embed(title='Error!', description='That is way too many cogs!', color=discord.Color.red())
await ctx.message.author.send('', embed=embed)
else:
"""Command listing within a cog."""
found = False
for x in self.client.cogs:
for y in cog:
if x == y:
embed = discord.Embed(title=cog[0] + ' Command Listing', description=self.client.cogs[cog[0]].__doc__)
for c in self.client.get_cog(y).get_commands():
if not c.hidden:
embed.add_field(name=c.name, value=c.help, inline=False)
found = True
if not found:
"""Reminds you if that cog doesn't exist."""
embed = discord.Embed(title='Error!', description='How do you even use "' + cog[0] + '"?', color=discord.Color.red())
else:
await ctx.message.add_reaction(emoji='✉')
await ctx.message.author.send('', embed=embed)
except:
await ctx.send("Excuse me, I can't send embeds.")
I am using the rewrite branch if it helps. And my setprefix command IS NOT in a cog, unlike the rest of my commands. Thus why they appear a slight bit different.
Now I'm ASSUMING my issue is that the json are not closing, which I need to know how to make them close, but if that's not the issue, I would very much appreciate someone helping me figure out what's going wrong.

Checking message.content for certain text

I'm trying to make a multiple-choice trivia background task. I'm trying to await client.wait_for_message only for either the correct answer or the incorrect choices. I'm checking message.content but I'm missing something because no matter what anyone types, it says "incorrect answer" UNLESS it's the correct answer. If someone types something other than the incorrect answer or correct answer then I want it to ignore their text.
async def trivia_loop():
await client.wait_until_ready()
channel = client.get_channel("123456778999533")
triviamonies = random.randint(1250, 2550)
while not client.is_closed:
await client.send_message(channel, '**Category**: {}\n**Difficulty**: {}\n\n{}\n\n**Potential Answers:**\n{}'.format(category, formatdiff, formatquestion, '\n'.join(tup)))
winner = ''
def check(m):
return m.content.lower() == formatanswer.lower() and m.content.lower() != incorrectanswer1 or incorrectanswer2 or incorrectanswer3
while winner == '':
answerid = await client.wait_for_message(timeout=420, channel=channel, check=check)
if answerid is None:
winner = 1
await client.delete_message(questionsend)
await client.send_message(channel, 'No one answered correctly! The answer was {}'.format(formatanswer))
await asyncio.sleep(840)
elif get_triviaguessesTaken(answerid.author) < 1 and answerid.content.lower() == formatanswer.lower():
winner = 1
await client.delete_message(questionsend)
await client.send_message(channel, '**{}** is correct!\n{} earns **${:,}** !'.format(formatanswer, answerid.author.mention, triviamonies))
add_dollars(answerid.author, triviamonies)
add_trivias(answerid.author, 1)
await asyncio.sleep(900)
elif get_triviaguessesTaken(answerid.author) < 1 and answerid.content.lower() == incorrectanswer1 or incorrectanswer2 or incorrectanswer3:
await client.send_message(channel, '**{}** that is incorrect! Please try again on the next trivia question'.format(answerid.author.mention))
add_triviaguessesTaken(answerid.author, 1)
elif get_triviaguessesTaken(answerid.author) > 0 and answerid.content.lower() == incorrectanswer1 or incorrectanswer2 or incorrectanswer3:
await client.send_message(channel, '{} you have already tried to guess this trivia question. Try again on the next one!'.format(answerid.author.mention))
Discord.py Version:
From the looks of your code, you're still using the async version of d.py (v0.16.12), and you haven't moved to the more up-to-date and stable rewrite version of d.py.
I can highly recommend running pip install --upgrade discord.py for the most recent stable version. Make sure you do this and rewrite your code sooner rather than later, as it'll be less of a pain when you have less code to work with.
Wait_for() usage in rewrite:
The docs use an example in an on_message event, but I'll make an example here to suit your case with a command instead, so you have both ways available to you and can mix-and-match:
#bot.command(aliases=["trivia"])
async def starttrivia(ctx):
answers = ["paris", "london", "oslo"]
await ctx.send(f"What's the capital of France?\n\n*Potential answers:*\n{', '.join(answers)}")
correct = False
def check(m):
for a in answers:
if a in m.content.lower():
return True
while not correct:
try:
reply = await bot.wait_for("message", check=check, timeout=30)
if "paris" in reply.content.lower():
await ctx.send(f"Correct! {reply.author.mention} got the right answer!")
correct = True
else:
await ctx.send("Incorrect, try again!")
except asyncio.TimeoutError: # exception thrown when no reply is sent in time
await ctx.send("Nobody guessed for 30 seconds! The answer was: Paris")
break

How to join multiple channels using twitchdev python code

I'm trying to make translate bot using twitchdev python code: https://github.com/twitchdev/chat-samples/blob/master/python/chatbot.py
I was able to get messages from channels and send translated text, but I cannot join multiple channels.
What I did below is making list of channels and call using for loop, but it only join to the last channel.
I tried to make another list of channels in def on_welcome(self, c, e) but it also works on the last channel (when I print self.channels in def on_welcome(self, c, e) it printed blank list, and when I print self.channel it only printed last channel)
Any suggestions are welcome.
import sys
import irc.bot
import requests
import config
class TwitchBot(irc.bot.SingleServerIRCBot):
def __init__(self, username, client_id, token, channels):
for channel in channels
self.client_id = client_id
self.token = token
self.channel = '#' + channel
# Get the channel id, we will need this for v5 API calls
url = 'https://api.twitch.tv/kraken/users?login=' + channel
headers = {'Client-ID': client_id, 'Accept': 'application/vnd.twitchtv.v5+json'}
r = requests.get(url, headers=headers).json()
self.channel_id = r['users'][0]['_id']
# Create IRC bot connection
server = 'irc.chat.twitch.tv'
port = 6667
print 'Connecting to ' + server + ' on port ' + str(port) + '...'
irc.bot.SingleServerIRCBot.__init__(self, [(server, port, 'oauth:'+token)], username, username)
def on_welcome(self, c, e):
print 'Joining ' + self.channel
# You must request specific capabilities before you can use them
c.cap('REQ', ':twitch.tv/membership')
c.cap('REQ', ':twitch.tv/tags')
c.cap('REQ', ':twitch.tv/commands')
c.join(self.channel)
def on_pubmsg(self, c, e):
# If a chat message starts with an exclamation point, try to run it as a command
if e.arguments[0][:1] == '!':
cmd = e.arguments[0].split(' ')[0][1:]
print 'Received command: ' + cmd
self.do_command(e, cmd)
return
def do_command(self, e, cmd):
c = self.connection
# Provide basic information to viewers for specific commands
elif cmd == "raffle":
message = "This is an example bot, replace this text with your raffle text."
c.privmsg(self.channel, message)
elif cmd == "schedule":
message = "This is an example bot, replace this text with your schedule text."
c.privmsg(self.channel, message)
def main():
if len(sys.argv) != 5:
print("Usage: twitchbot <username> <client id> <token> <channel>")
sys.exit(1)
username = config.twitch['botname']
client_id = config.twitch['cliendID']
token = config.twitch['oauth']
channels = ["channel1", "channel2"]
bot = TwitchBot(username, client_id, token, channels)
bot.start()
if __name__ == "__main__":
main()
In your main() function, you are establishing an object of the bot:
bot = TwitchBot(username, client_id, token, channels)
Instead of passing multiple channels to the same bot object, create multiple bot objects with single channels. I haven't tested this yet because Twitch updated their OAuth protocol and I haven't been through the docs.
You may need to thread them, and depending on what functions you implement it may not be thread-safe (just test before going into production). That'd probably look something like:
import threading
t1 = threading.Thread(target=TwitchBot, args=(username, client_id, token, channel1))
t2 = threading.Thread(target=TwitchBot, args=(username, client_id, token, channel2))
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
Once again, I haven't tested any of this but will likely have time in a few weeks to patch this in my own code after the holiday.

Trying to get user input with MessageHandler using ConversationHandler

im trying to deal with both InlineKeyboardButton callback_data, and free text data..here's my scenario:
prompting an InlineKeyboard with several buttons, the user clicks one button and then asked to input some free text for the BE to be used.
I have tried to use CallbackQueryHandler(several InlineKeyboardMarkup in the callback function) as an entry point for ConversationHandler which then trigger MessageHandler, with no much success..
I need to catch the free text update (basically wait for user input).
def start(update, context):
keyboard = [[InlineKeyboardButton("bal bla", callback_data='1'),
InlineKeyboardButton("bla bla", callback_data='2')],
[InlineKeyboardButton("bla bla)", callback_data='3'),
InlineKeyboardButton("bla bla", callback_data= '4')],
[InlineKeyboardButton("bla bla", callback_data='5')]]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
reply_text = {'text': ''}
def reply_message(update, context):
message = update.message.text
reply_text['text'] = message
return reply_text['text']
def button(update, context, user_data):
query = update.callback_query
query.edit_message_text(text="Loading....\n \r")
if query.data == '1':
pass
elif query.data == '2':
pass
elif query.data == '3':
keyboard = [[InlineKeyboardButton('BBB', callback_data='21'),
InlineKeyboardButton('GGG', callback_data='22')],
[InlineKeyboardButton('PPP', callback_data='23')]]
reply_markup1 = InlineKeyboardMarkup(keyboard)
query.edit_message_text('Please select:', reply_markup=reply_markup1)
elif query.data == '21':
query.edit_message_text('input customer name ')
return 1
if : #no idea which condition to give here
print(reply_text['text'], '\n ^ new free text message here ^')
def main():
conv_handler= ConversationHandler(
entry_points=[
CallbackQueryHandler(button)
],
states={
1 : [MessageHandler(Filters.text, reply_message)],
},
fallbacks= []
)
try:
updater = Updater(bot_token, use_context=True)
updater.dispatcher.add_handler(CommandHandler('start', start))
updater.dispatcher.add_handler(CallbackQueryHandler(button))
updater.dispatcher.add_handler(conv_handler)
updater.start_polling()
updater.idle()
except Exception as e:
print(e)
Hey i solved this with making a function to parse the user input and remember the input with the user_data method of context.
def check_user_input(update, context):
# get last user text
user_input = update.message.text
# get user_data context
user_data = context.user_data
# set new context user_input
user_data['user_input'] = user_input
context.user_data['user_input'] = user_data['user_input']
if "Text" in user_input:
# do something... i.e reply w/o keyboard
update.message.reply_text(
("{}?!, sounds interesting, tell me more".format(
user_input)))
return GUEST_CHOICE
else:
# ask again
reply_keyboard = [['Text'],['Done']]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
update.message.reply_text(
("{}?!, i dont know anything...".format(
user_input),reply_markup=markup))
return CHECK_CHOICE
This seems to be the favorized method to handle and remember user inputs.
This function can now be called within the ConversationHandler/MessageHandlers as you need. You only have to return to a specified ConversationHandler state. So i will make two states, a GUEST_CHOICE and a CHECK_CHOICE state, to handle a single user input validation as example.
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
# Ask Guest
GUEST_CHOICE: [MessageHandler(Filters.regex('^(Text)$'),
guest_choice),
],
# Verify input
CHECK_CHOICE: [MessageHandler(Filters.text,
check_user_input),
],
},
fallbacks=[MessageHandler(Filters.regex('^Done$'), done, CommandHandler('start', start))],
allow_reentry = False,
per_user=True,
conversation_timeout=3600,
name="test"
)
As defined, /start is used as user to start the conversationhandler. This calls a start() method i.e.:
def start(update, context):
reply_keyboard = [['Text'],['Done']]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
# Setup keyboard and reply
update.message.reply_text(
("Hey {}, how can i help you?".format(
update.message.chat['first_name'])),
reply_markup=markup)
return CHECK_CHOICE
start() will setup the markup keyboard and any input after will be handled with CHECK_CHOICE state, which will call check_user_input here (see above). This will return GUEST_CHOICE and so it will call guest_choice() i.e.:
def guest_choice(update, context):
user_data = context.user_data
# Check authentication request
if "bob" in user_data['user_input']:
update.message.reply_text("Hey {}".format(user_data['user_input']))
else:
update.message.reply_text("Where is bob?!")
return CHECK_INPUT
The Done method will clean up the conversation
def done(update, context):
markup = get_markup(context)
update.message.reply_text("Bye")
return ConversationHandler.END
Ok that escalated quickly... I rewrote that code from my own working version of a conversation handler, please check for bugs :) Maybe the user_data has to be cleared in the Done method.

How to "temporary ban" someone with discord.py?

i am making a management discord bot with discord.py, so i realized that i need to add a command to temp ban someone for some time, this ban can be by roles or by kicking the member out of the channel and then banning him, but i don't know how to do that. Can someone help me?
After a lot of trial and error I finally got it! Given bellow is a discord.py bot with a command to temporarily ban a user and can be used for multiple users
ban_list = []
day_list = []
server_list = []
#This is a background process
async def countdown():
await client.wait_until_ready()
while not client.is_closed:
await asyncio.sleep(1)
day_list[:] = [x - 1 for x in day_list]
for day in day_list:
if day <= 0:
try:
await client.unban(server_list[day_list.index(day)], ban_list[day_list.index(day)])
except:
print('Error! User already unbanned!')
del ban_list[day_list.index(day)]
del server_list[day_list.index(day)]
del day_list[day_list.index(day)]
#Command starts here
#client.command(pass_context = True)
async def ban(ctx,member:discord.Member, days = 1):
if str(ctx.message.author.id) == '<You ID goes here>':
try:
await client.ban(member, delete_message_days=0)
await client.say('User banned for **' + str(days) + ' day(s)**')
ban_list.append(member)
day_list.append(days * 24 * 60 * 60)
server_list.append(ctx.message.server)
except:
await client.say('Error! User not active')
else:
await client.say('You do not have permission to ban users!')
client.loop.create_task(countdown())
I tested this program by banning three users for distinct amounts of time and it worked like a charm. Please note that the time may not be too accurate. The greater the time you choose, the greater the error.
For some reason users that are offline cannot be banned by a Bot.
The bot has to be online full time for this to work... If you reboot the bot or the bot crashes all lists get cleared.
It depends on what you mean by "temporary ban".
Do you want the user actually kicked out and banned from the server for a certain period of time, or do you want the user to be temporarily restricted from certain permissions such as chatting?
I recommend the latter and using the Discord rewrite branch of the API which is new and improved.
Restrict a member via role assignment and unrestrict after x seconds:
#bot.command()
async def restrict(ctx, member:discord.Member, duration: int):
role = discord.utils.get(ctx.guild.roles, name="Restricted")
await member.add_roles(role)
await asyncio.sleep(duration)
await member.remove_roles(role)
Ban a user and unban after x seconds:
#bot.command()
async def ban(ctx, user:discord.User, duration: int):
await ctx.guild.ban(user)
await asyncio.sleep(duration)
await ctx.guild.unban(user)
Keep in mind, if your bot crashes or goes offline for whatever reason while it's in the process of sleeping to unban a user, the bot will not unban the user after it comes back up, so something to consider using may be a database and storing the end time of the ban. You can then query all saved dates during bot startup to figure out how long to sleep for. Also, you will have to get their User object rather than Member object as they are not a part of the guild anymore.

Resources