Why has is_closed stopped working in the rewrite version? - python-3.x

I am new to the Discord python API. I am trying to run a method forever as long as the client is still open. I scheduled the method through Client.loop.create_task(), and am running while not client.is_closed. I added a debugging print statement, but the print statement is called 0 times.
I based the code on this post: Discord.py Schedule
import discord
from datetime import datetime
from datetime import timedelta
token = "TOKEN"
s_users = {}
timezon_offset = 5
inac_days = 0
inac_hours = 0
inac_minutes = 1
active_role = "Active"
inac_role = "Inactive"
client = discord.Client()
async def monitor():
for guild in client.guilds:
for user in guild.members:
s_users[user] = datetime(1,1,1)
#client.event
async def on_ready():
print('logged on as {0}'.format(client.user))
#client.event
async def on_message(message):
print('message logged from {0.author}: {0.content} at {0.created_at}'.format(message))
s_users[message.author] = message.created_at - timedelta(hours=timezon_offset)
async def task():
await client.wait_until_ready()
active = None
inactive = None
for guild in client.guilds:
for role in guild.roles:
if (role.name == active_role):
active = role
elif (role.name == inac_role):
inactive = role
while not client.is_closed:
print("here")
for user in s_users:
if datetime.now() - s_users[user] > timedelta(days=inac_days, hours=inac_hours, minutes=inac_minutes):
if not(inac_role in [role.name for role in user.roles]):
await user.remove_roles(active)
await user.add_roles(inactive)
print("gave user: " + user.name + " " + inac_role + " role")
if datetime.now() - s_users[user] <= timedelta(days=inac_days, hours=inac_hours, minutes=inac_minutes):
if not(active_role in [role.name for role in user.roles]):
await user.remove_roles(inactive)
await user.add_roles(active)
print("gave user: " + user.name + " " + active_role + " role")
client.loop.create_task(task())
client.run(token)
It should execute task(), which should run as long as the client is not closed. However, the print statement is executed 0 times.

In version 1.0.0, Client.is_closed was changed from a property to a method. See Property Changes in the Migration guide.
You'll need to add parentheses to call the method:
while not client.is_closed():
...

Related

Discord.py: Access files on the computer of the person who sent a message. Play a song from the local drive of any user on the server

I have a bot that can play music from my computer. Is there a way to play a song from the computer of the person who sent the message? My thought would be using message.author to somehow access the person's session and get into their drive. Here is my bot. It can join a voice channel, create playlists from local file paths, start a playlist or individual file with stop/pause/play/next/previous controls:
import discord
import os.path
import logging
import asyncio
from os import path
global ready
global vc
global source
global songQueue
global songIndex
global commandList
global stopPlaylist
ready = False
stopPlaylist = False
songQueue = []
songIndex = 0
logger = logging.getLogger('discord')
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(filename='D:\DnD\DiscordBot\discord.log', encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
logger.addHandler(handler)
client = discord.Client()
commands = [
'!connect\nConnect to a voice channel by channel id. Use !channels to find the desired id.\nExample Command: !connect 827202170973323305\n\n',
'!channels\nLists all voice channels and their connection id.\n\n',
'!add\nAdd a file path to the playlist.\nExample Command: !add D:\\DnD\\DiscordBot\\mySong.mp3\n\n',
'!delete\nDeletes the last song added to the playlist.\n\n',
'!view\nDisplays the current playlist, in order.\n\n',
'!playlist\nStarts the playlist from the beginning, or optionally add a number as the start position.\nExample Command: !playlist 3\n\n',
'!playSong\nPlays a specified file.\nExample Command: !playSong D:\\DnD\\DiscordBot\\mySong.mp3\n\n',
'!next\nPlays next song in playlist.\n\n',
'!prev\nPlays previous song in playlist.\n\n',
'!stop\nStops all music song. Playlist will restart from the beginning.\n\n',
'!pause\nPauses the current song. Restart with !resumeSong.\n\n',
'!resume\nResumes the current song.\n\n'
'!status\nLets you know if the bot thinks it is playing music.'
]
commandList=''
for command in commands:
commandList+=command
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#client.event
async def on_message(message):
global ready
global vc
global source
global songQueue
global songIndex
global commandList
global stopPlaylist
if message.author == client.user:
return
#!help
if message.content.startswith('!help'):
await message.channel.send('{0}'.format(commandList))
return
#!connect
if message.content.startswith('!connect'):
if ready:
await message.channel.send('Bot [{0}] is already connected to a voice channel.'.format(client.user))
return
channel = int(message.content[9:])
vc = await client.get_channel(channel).connect()
ready = True
await message.channel.send('Bot [{0}] is connecting to voice.'.format(client.user))
return
#!channels
if message.content.startswith('!channels'):
channelList = ''
for channel in client.get_all_channels():
if channel.type == discord.ChannelType.voice:
channelList += 'name: ' + channel.name + '\n'
channelList += 'id: ' + str(channel.id) + '\n\n'
await message.channel.send('{0}'.format(channelList))
return
#!add
if message.content.startswith('!add'):
song = message.content[5:]
if not path.exists(song):
await message.channel.send('Song not found or invalid path specified.\nSpecified Path: {0}\nExample command: !addSong C:\\Users\\Public\\Music\\mySong.mp3'.format(song))
return
songQueue.append(song)
await message.channel.send('Song added: {0}\nCurrent playist length: {1} song(s)'.format(song,len(songQueue)))
return
#!delete
if message.content.startswith('!delete'):
if len(songQueue) == 0:
await message.channel.send('Playlist is empty. Use !addSong, !viewList, and !playList to manage playlists.')
return
await message.channel.send('Removed song: {0}'.format(songQueue.pop()))
return
#!view
if message.content.startswith('!view'):
if len(songQueue) == 0:
await message.channel.send('Playlist is empty. Use !addSong, !deleteSong, and !playList to manage playlists.')
return
await message.channel.send('Current Playlist:\n{0}'.format('\n'.join(songQueue)))
return
#play commands
if message.content.startswith('!play'):
if not ready:
await message.channel.send('Bot [{0}] is not connected to a voice channel.'.format(client.user))
return
#!playlist
if message.content.startswith('!playlist'):
try:
songIndex = int(message.content[10:]) - 1
if songIndex >= len(songQueue):
songIndex = len(songQueue) - 1
except:
pass
playSong()
return
#!playSong
if message.content.startswith('!playSong'):
song = message.content[10:]
if not path.exists(song):
await message.channel.send('Song not found or invalid path specified.\nSpecified Path: {0}\nExample command: !play C:\\Users\\Public\\Music\\mySong.mp3'.format(song))
return
source = discord.FFmpegPCMAudio(song)
vc.play(source, after=None)
await message.channel.send('Playing song: {0}'.format(song))
return
#!next
if message.content.startswith('!next'):
vc.stop()
#!prev
if message.content.startswith('!prev'):
songIndex -= 2
if songIndex < -1:
songIndex = -1
vc.stop()
#!stop
if message.content.startswith('!stop'):
if not ready:
await message.channel.send('Bot [{0}] is not connected to a voice channel.'.format(client.user))
return
vc.stop()
songIndex = 0
stopPlaylist = True
await message.channel.send('Stopping music.')
return
#!pause
if message.content.startswith('!pause'):
if not ready:
await message.channel.send('Bot [{0}] is not connected to a voice channel.'.format(client.user))
return
vc.pause()
await message.channel.send('Pausing music.')
return
#!resume
if message.content.startswith('!resume'):
if not ready:
await message.channel.send('Bot [{0}] is not connected to a voice channel.'.format(client.user))
return
vc.resume()
await message.channel.send('Resuming music.')
return
#!status
if message.content.startswith('!status'):
if not ready:
await message.channel.send('Bot [{0}] is not connected to a voice channel.'.format(client.user))
return
if vc.is_playing():
await message.channel.send('Something is playing.')
return
await message.channel.send('Nothing is playing.')
return
def playSong():
global songQueue
global songIndex
global vc
try:
song = songQueue[songIndex]
source = discord.FFmpegPCMAudio(song)
vc.play(source, after=nextSong)
except Exception as e:
print('playSong error {0}'.format(e))
def nextSong(error):
global songQueue
global songIndex
global stopPlaylist
try:
songIndex += 1
if songIndex >= len(songQueue):
stopPlaylist = True
if stopPlaylist:
songIndex = 0
stopPlaylist = False
return
futureFunction = asyncio.run_coroutine_threadsafe(playSong(), client.loop)
futureFunction.result()
except Exception as e:
print('nextSong error {0}'.format(e))
##client.event
#async def on_logout(user)
# global ready
# if user == client.user:
# ready = False
client.run('TOKEN')
Ok, the thing you must know is that you can access your local drive cause the bot runs on it, but of course you can't access other users drive (it will be very dangerous). Neither discord.py and Discord allow it.

Discord Poll Bot

Hi i am trying to make a poll bot but ive encountered a problem here is the code ignore the other commands other than + poll
import discord
import os
import requests
import json
import random
pollid = 0
emoji1 = '\N{THUMBS UP SIGN}'
emoji2 = '\N{THUMBS DOWN SIGN}'
client = discord.Client()
sad_words=["sad","depressed", "unhappy","angry","miserable","depressing"]
starter_encouragements = ["Cheer up", "hang in there.", "You are a great person / bot!"]
def get_quote():
response = requests.get("https://zenquotes.io/api/random")
json_data = json.loads(response.text)
quote = json_data[0]['q'] + " -" + json_data[0]['a']
return[quote]
from discord.utils import get
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
#client.event
async def on_message(message):
global pollid
#if message.author == client.user:
# return
msg = message.content
if message.content.startswith('+encourage'):
quote=get_quote()
await message.channel.send(quote)
if any(word in msg for word in sad_words):
await message.channel.send(random.choice(starter_encouragements))
if message.content.startswith("+joke"):
from dadjokes import Dadjoke
dadjoke = Dadjoke()
await message.channel.send(dadjoke.joke)
if message.content.startswith("+poll"):
pollid = pollid+1
await message.channel.send(content = msg.split(' ', 1)[1] + ' (number of polls made ' + str(pollid) + ')')
if message.author == client.user:
await message.add_reaction(emoji1)
await message.add_reaction(emoji2)
reaction = get(message.reactions, emoji=emoji1)
#reaction2 = get(message.reactions, emoji=emoji2)
#if (reaction != None and reaction2 != None):
# totalcount = reaction.count + reaction2.count
#if totalcount>=2:
if (reaction != None and reaction.count != 1):
await message.channel.send('The outcome of the poll is yes'+ str(reaction.count))
# await message.channel.send('The outcome of the poll is no')
client.run(os.getenv('TOKEN'))
i am very new to python and the discord api for it ive been trying to set up a poll system where it has a timer on each poll that lasts 24 hrs and after 24 hrs it compares the amount of reactions on the message to see which side wins. Can someone help me with this. Thanks
I would not use on_message events for that but instead use a command.
You can do something like this:
import discord
from discord.ext import commands
#client.command()
async def poll(ctx, *, text: str):
poll = await ctx.send(f"{text}")
await poll.add_reaction("✅")
await poll.add_reaction("❌")
Here we have text as a str so you can add as many text as you want.
If you want to compare it after 24 hours you would also have to build in a cooldown saver if the bot restarts, otherwise we could go with await asyncio.sleep(TimeAmount)
If you want to check the result with a command we could go for this:
from discord.utils import get
import discord
from discord.ext import commands
#client.command()
async def results(ctx, channel: discord.TextChannel, msgID: int):
msg = await channel.fetch_message(msgID)
reaction = get(msg.reactions, emoji='✅')
count1 = reaction.count # Count the one reaction
reaction2 = get(msg.reactions, emoji="❌")
count2 = reaction2.count # Count the second reaction
await ctx.send(f"✅**: {count1 - 1}** and ❌**: {count2 - 1}**") # -1 is used to exclude the bot
The usage of the command would be: results #channel MSGID.
Be aware that fetch is an API call and could cause a ratelimit.

How can I make the bot write on how many servers it is active in its status? (Discord.py Rewrite)

What I want to do is actually the bot to write both my messages and how many servers are online.
My Code:
status = cycle([f"I am online on {len(bot.guilds)} servers!","Second Status")
#bot.event
async def on_ready():
print("Logged in as: " + bot.user.name + "\n")
await change_status.start()
#tasks.loop(seconds = 30)
async def change_status():
await bot.change_presence(activity = discord.Game(next(status)))
When I try that, Bot status is as follows:
I am online on 0 servers!
and every time the loop repeats, the bot says:
I am online on 0 servers!
But the bot is currently online on 3 servers. How can I fix this?
Define status in the function, so it's constantly updating and loads after the bot is ready.
status = 0
#bot.event
async def on_ready():
print("Logged in as: " + bot.user.name + "\n")
await change_status.start()
#tasks.loop(seconds = 30)
async def change_status():
statuses = [f"I am online on {len(bot.guilds)} servers!", "Second Status"]
if status + 1 > len(statuses):
status = 0
await bot.change_presence(activity = discord.Game(statuses[status]))
status += 1

How to loop a task in discord.py

I am experimenting with making my own little discord bot that can get information from Twitch, but I'm stumped on how to make the bot loop and check for a condition.
I want the bot to loop a section of code every few seconds that checks if the specified twitch channel is live.
Code
import discord
from discord.ext import commands, tasks
from twitch import TwitchClient
from pprint import pformat
client = TwitchClient(client_id='<twitch token>')
bot = commands.Bot(command_prefix='$')
#bot.event
async def on_ready():
print('We have logged in as {0.user}'.format(bot))
#bot.command()
async def info(ctx, username):
response = await ctx.send("Querying twitch database...")
try:
users = client.users.translate_usernames_to_ids(username)
for user in users:
print(user.id)
userid = user.id
twitchinfo = client.users.get_by_id(userid)
status = client.streams.get_stream_by_user(userid)
if status == None:
print("Not live")
livestat = twitchinfo.display_name + "is not live"
else:
livestat = twitchinfo.display_name + " is " + status.stream_type
responsemsg = pformat(twitchinfo) + "\n" + livestat
await response.edit(content=responsemsg)
except:
await response.edit(content="Invalid username")
bot.run("<discord token>")
I want the bot to run the following code every 10 seconds, for example:
status = client.streams.get_stream_by_user(<channel id>)
if status == None:
print("Not live")
livestat = twitchinfo.display_name + "is not live"
else:
livestat = twitchinfo.display_name + " is " + status.stream_type
I've tried using #tasks.loop(seconds=10) to try and make a custom async def repeat every 10 seconds but it didn't seem to work.
Any ideas?
The newer version of discord.py doesn't support client.command()
To achieve the same I used the following snippet
import discord
from discord.ext import tasks
client = discord.Client()
#tasks.loop(seconds = 10) # repeat after every 10 seconds
async def myLoop():
# work
myLoop.start()
client.run('<your token>')
This can be done like so:
async def my_task(ctx, username):
while True:
# do something
await asyncio.sleep(10)
#client.command()
async def info(ctx, username):
client.loop.create_task(my_task(ctx, username))
References:
asyncio.create_task()
asyncio.sleep()
This is the most proper way to implement background tasks.
from discord.ext import commands, tasks
bot = commands.Bot(...)
#bot.listen()
async def on_ready():
task_loop.start() # important to start the loop
#tasks.loop(seconds=10)
async def task_loop():
... # this code will be executed every 10 seconds after the bot is ready
Check this for more info
I struggled with this as well. The problem I ran into is that none of the examples online were complete. Here is one I came up with that uses #tasks.loop(seconds=10).
import discord
import os
from discord.ext import tasks
from dotenv import load_dotenv
intents = discord.Intents.all()
client = discord.Client(command_prefix="!", intents=intents)
load_dotenv()
token = os.getenv("DISCORD_TOKEN")
CHANNEL_ID = 1234
#client.event
async def on_ready():
print(f"We have logged in as {client.user}")
myloop.start()
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith("hi"):
await message.channel.send("Hello!")
#tasks.loop(seconds=10)
async def myloop():
channel = client.get_channel(CHANNEL_ID)
await channel.send("Message")
client.run(token)

Invoke a bot message when the user has not specified a nickname

I want my bot to write
Error, you did not specify a nickname.
My code:
if message.content.startswith(prefix + 'kill'):
name = message.mentions[0]
randomgif = random.choice(["https://i.imgur.com/Pk6lOwI.gif","https://i.imgur.com/IRoF2pJ.gif","https://i.imgur.com/dV2N6KJ.gif","https://i.imgur.com/xyhP7Rz.gif","https://i.imgur.com/CzpHqFX.gif","https://i.imgur.com/iBsrA1c.gif","https://i.imgur.com/4jHevGp.gif","https://i.imgur.com/Oy2XGuL.gif","https://i.imgur.com/1uQwyli.gif","https://i.imgur.com/uCUf0V5.gif","https://i.imgur.com/xfubfyv.gif","https://i.imgur.com/8th0trO.gif","https://i.imgur.com/2jOXQcP.gif","https://i.imgur.com/2kCGfie.gif","https://i.imgur.com/TPV6Upz.gif","https://i.imgur.com/Bti43x8.gif"])
embed=discord.Embed(color=0x008C8C)
embed.set_author(name= message.author.display_name + " убил " + name.display_name, icon_url='https://pre00.deviantart.net/7e08/th/pre/f/2017/125/2/2/felix_argyle_by_aramisdraws-db8847i.png')
embed.set_image(url= randomgif)
await client.send_message(message.channel, embed=embed)
Member.nick is the server-specific nickname of a member. If there is no such name, it will be None.
import discord
from discord.ext import commands
import random
bot = commands.Bot(command_prefix=prefix)
#bot.command(pass_context=True)
async def kill(ctx, member: discord.Member):
if member.nick is None:
await bot.say("Error! You did not specify a nickname")
return
randomgif = random.choice(["https://i.imgur.com/Pk6lOwI.gif","https://i.imgur.com/IRoF2pJ.gif","https://i.imgur.com/dV2N6KJ.gif","https://i.imgur.com/xyhP7Rz.gif","https://i.imgur.com/CzpHqFX.gif","https://i.imgur.com/iBsrA1c.gif","https://i.imgur.com/4jHevGp.gif","https://i.imgur.com/Oy2XGuL.gif","https://i.imgur.com/1uQwyli.gif","https://i.imgur.com/uCUf0V5.gif","https://i.imgur.com/xfubfyv.gif","https://i.imgur.com/8th0trO.gif","https://i.imgur.com/2jOXQcP.gif","https://i.imgur.com/2kCGfie.gif","https://i.imgur.com/TPV6Upz.gif","https://i.imgur.com/Bti43x8.gif"])
embed=discord.Embed(color=0x008C8C)
embed.set_author(name= ctx.message.author.display_name + " убил " + member.display_name, icon_url='https://pre00.deviantart.net/7e08/th/pre/f/2017/125/2/2/felix_argyle_by_aramisdraws-db8847i.png')
embed.set_image(url= randomgif)
await bot.say(embed=embed)
If you're implementing commands, you should be using the discord.ext.commands module, that's what it's for.

Resources