How to "temporary ban" someone with discord.py? - python-3.x

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.

Related

AttributeError: 'ChatForbidden' object has no attribute 'access_hash'

I have made a telegram member scraper and inviter with Python. It was successful on some tests, but there are times my accounts get banned with upon seeing this error message:
AttributeError: 'ChatForbidden' object has no attribute 'access_hash'
I'm not sure why would it show ChatForbidden if I am already an admin of a group. It's hard to test these as I had to buy new phone numbers every time.
Here's a sample and explanation of my code to invite members to a group:
# Log in into the telegram account
client = TelegramClient('Tg_scraper', api_id, api_hash)
chats = []
last_date = None
chunk_size = 200
groups = []
hash_list = []
# Get all the groups/channel of the account
result = client(GetDialogsRequest(
offset_date=last_date,
offset_id=0,
offset_peer=InputPeerEmpty(),
limit=chunk_size,
hash=0
))
chats.extend(result.chats)
# Puts all the group/channel into a list
i = 0
print('Enter a NUMBER to choose a group where the members will be invited into:')
for chat in chats:
try:
groups.append(chat)
hash_list.append(chat.access_hash)
print(f"({i})" + ' - ' + chat.title)
i += 1
except:
continue
g_index = input("Enter a Number: ")
target_group = groups[int(g_index)]
target_group_entity = InputPeerChannel(target_group.id, target_group.access_hash)
Upon the last line, target_group_entity = InputPeerChannel(target_group.id, target_group.access_hash) is where I encounter the error I have stated above. Upon receiving that error, I get banned.
Does this have something to do with permissions? Do new accounts get banned for botting? It works on my first few tests, but then now I can't invite. Thank you so much for anyone who could help in advance.
I am already an admin of a group
This error is unrelated to your permission level.
Upon the last line is where I encounter the error
Wrong. you encounter this error because you're not coding it right with types in mind, expecting all your .chats are groups. Telegram doesn't tell you what fields exactly have, as you see in this error.
You must use type checking to limit your chats objects to only what you expect, your try block is appending then erroring, so, rather than a plain:
except:
continue
you need to actually confirm it won't error when accessing fields.
print('Enter a NUMBER to choose a group where the members will be invited into:')
i = 0
for chat in chats:
if isinstance(chat, telethon.types.Channel):
if chat.broadcast: continue # ignore non-group
groups.append(chat)
hash_list.append(chat.access_hash)
print(f"({i})" + ' - ' + chat.title)
i += 1
g_index = input("Enter a Number: ")
target_group = groups[int(g_index)]

Discord connection lost Pycord

I'm working on a chat-revive bot that waits for a message (for an hour) before sending an embed with a chat revive ping. It does this correctly with a small waiting time. i.e if I put 5 - 10 minutes (300 - 600 seconds) on the wait_for section. But anything higher than about 30 minutes and the bot doesn't respond after the time is up. I've been told it's most likely from a reconnection and it cleared the task. I was wondering if anyone knew how to either keep the socket alive long enough to finish the said task or how to resume from where it left off after it disconnects then reconnects
#bot.event
async def on_message(ctx):
if ctx.channel.id == 991806371815243836:
pass
else:
return
if ctx.author.id != bot.user.id:
pass
if not ctx.author.bot:
pass
print("yes")
else:
return
#everything above is just for specifications
guild = bot.get_guild(920057625255747674)
chn = guild.get_channel(991806371815243836)
times = [10, 5, 15]
async def wait():
await bot.wait_for("message", timeout=3600) # Waits an hour for someone to send message
try:
loop = asyncio.get_event_loop()
task = loop.create_task(wait())
shields = shield(task)
shields.cancel()
await wait()
except asyncio.TimeoutError: # If returned with a timeout error, it would send the chat revive embed
async with chn.typing():
await asyncio.sleep(random.choice(times))
embed = discord.Embed(title="Come Chat!",
description="Aether is quiet,, it must be asleep :o Let's wake it up! Come talk to us!",
color=0xBEE0FA)
await chn.send("<#&982394594727718938>", embed=embed)
bot.run(token)

How to get deleted users from channel (telethon)?

In telegram when I click Subscribers it shows me about 50 last users and about 150-200 deleted users.
I tried this:
async for user in client.iter_participants(chat_id):
if user.deleted:
print(user)
This gives me only last 50 users and 6-8 deleted users. I need all 150-200 deleted users. How can I get them?
I solved this problem using GetParticipantsRequest with offset parameter somehow like this:
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
chat_id = -123456
offset = 0
while True:
participants = await client(GetParticipantsRequest(
channel=chat_id,
filter=ChannelParticipantsSearch(''),
offset=offset,
limit=10000,
hash=0
))
deleted_users = []
for user in participants:
if user.deleted:
deleted_users.append(user)
if not deleted_users:
break
# doings with deleted_users
Not sure about iter_participants, but get_participants works in my case.
channel_id = -1234567890 # TODO: add channel id
users = client.get_participants(client.get_input_entity(channel_id))
for user in users:
if user.deleted:
print(user)

Message repeating even after explicitly checking for history in discord

I'm trying to make a discord bot and I've added functionality of the tasks repeating after 24 hours using tasks.loop after at an interval of 24 hours( 64800 seconds). Since the code is run on repl.it, I've used uptime robot to continuously send a ping after 5 minutes to make it running always in the repl.it.
My issue is that even after checking historyin the channel the message is getting send again as you can see in the below image. ( by checking here I mean the explanation which is the explanation that comes with the API). I'm using NASA api here.
Code snippet
def get_APOD():
image = requests.get("https://api.nasa.gov/planetary/apod?api_key="+apod)
json_data = json.loads(image.text)
description = json_data["url"]
explanation = "'" + json_data["explanation"]+ "'"
title = json_data["title"]
return description,explanation,title
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
send_message.start()
#tasks.loop(seconds=64800.0)
async def send_message():
channel = client.get_channel(int(apod_channel))
messages = await channel.history(limit=1).flatten()
description,explanation,title = get_APOD()
if messages[0].content == explanation:
print("Its the same")
else:
await channel.send(description)
await channel.send(title)
await channel.send(explanation)
. Here you can see the messages repeating twice. It shouldn't by the code I've written here. Since it check for the history in the channel.
What is the issue here? Appreciate any help.
EDIT: When I restart the repl.it IDE the code will run alright and print "It's the same" as it should if the message is already sent but otherwise it's failing.

How can you reset or cancel asyncio.sleep events?

I am trying to set up a bot in discord that works on timers. One of them will work if one person types in the '!challenge' command, in which the bot will wait for 60 seconds to see if anyone types the '!accept' command in response. If it does not, it states 'Challenge was ignored. Resetting.' Or something along those lines.
Another timer actually runs during the game itself, and is an hour long. However, the hour resets after a command has been put in. If the games is idle for an hour (One player DCs or quits) the bot resets the game itself.
I had this working with threading:
# Initiate a challenge to the room. Opponent is whoever uses the !accept command. This command should be
# unavailable for use the moment someone !accepts, to ensure no one trolls during a fight.
if message[:10] == "!challenge":
msg, opponent, pOneInfo, new_game, bTimer, playerOne = message_10_challenge(channel, charFolder, message, unspoiledArena, character, self.game)
if opponent is not "":
self.opponent = opponent
if pOneInfo is not None:
self.pOneInfo = pOneInfo
self.pOneTotalHP = self.pOneInfo['thp']
self.pOneCurrentHP = self.pOneInfo['thp']
self.pOneLevel = self.pOneInfo['level']
if new_game != 0:
self.game = new_game
if bTimer is True:
timeout = 60
self.timer = Timer(timeout, self.challengeTimeOut)
self.timer.start()
if playerOne is not "":
self.playerOne = playerOne
super().MSG(channel, msg)
and:
# Response to use to accept a challenge.
if message == "!accept":
msg, pTwoInfo, new_game, playerTwo, bTimer, bGameTimer, new_oppenent, token = message_accept(channel, charFolder, unspoiledArena, character, self.game, self.opponent, self.pOneInfo)
if not charFile.is_file():
super().MSG(channel, "You don't even have a character made to fight.")
else:
if new_game is not None:
self.game = new_game
if pTwoInfo is not None:
self.pTwoInfo = pTwoInfo
self.pTwoTotalHP = self.pTwoInfo['thp']
self.pTwoCurrentHP = self.pTwoInfo['thp']
self.pTwoLevel = self.pTwoInfo['level']
if bTimer:
self.timer.cancel()
if bGameTimer:
gametimeout = 3600
self.gameTimer = Timer(gametimeout, self.combatTimeOut)
self.gameTimer.start()
if new_oppenent is not None:
self.opponent = new_oppenent
if playerTwo is not None:
self.playerTwo = playerTwo
if token is not None:
self.token = token
for msg_item in msg:
super().MSG(channel, msg_item)
with functions:
def challengeTimeOut(self):
super().MSG(unspoiledArena, "Challenge was not accepted. Challenge reset.")
self.game = 0
def combatTimeOut(self):
super().PRI('Unspoiled Desire', "!reset")
The above is an example of the same game, but on a different chat platform, with threading to handle the timers. But threading and discord.py aren't friends I guess. So I am trying to get the above code to work with discord.py, which seems to use asyncio.
The thought was to use asyncio.sleep() in the
if bTimer is true:
await asyncio.sleep(60)
self.game = 0
await ctx.send("Challenge was not accepted. Challenge reset.")
And this works...but it doesn't stop the timer, so even if someone !accepts, thus changing bTimer to False, which would cancel the timer:
if bTimer:
self.timer.cancel()
it's still going to say "Challenge was not accepted. Challenge reset."
The same problem will occure with bGameTimer if I try:
if bGameTimer:
await asyncio.sleep(3600)
await ctx.send("!reset")
the game will be hardwired to reset after 1 hours time, no matter if the game is done or not. Rather than resetting the 1 hour timer after every turn, and ONLY resetting if a full hour has passed in which no commands are made.
Is there a way to easily cancel or reset sleep cycles?
The asyncio code equivalent to your threading code would be:
...
if bTimer:
self.timer = asyncio.create_task(self.challengeTimeout())
...
async def challengeTimeout(self):
await asyncio.sleep(60)
self.game = 0
await ctx.send("Challenge was not accepted. Challenge reset.")
asyncio.create_task() creates a light-weight "task" object roughly equivalent to a thread. It runs "in parallel" to the your other coroutines, and you can cancel it by invoking its cancel() method:
if bTimer:
self.timer.cancel()

Resources