0

I am making a discord bot for my friends that checks if something is in stock on a website and sends a message to a discord channel if it finds stock. I am trying to run the ne and bB methods simultaneously as well as independent of each other. I receive "Timeout context manager should be used inside a task" when the "await channel.send" lines are run in their respective methods. This line should send cause the discord bot to send a message to the specified channel. Otherwise it works fine. I have tried running them as task loops per the discord documentation with no luck. I have also researched extensively alternative methods to achieve the same goal but keep hitting dead ends. Is there anyway to achieve this in one .py file or am I going to need a re-write. Admittedly I probably don't understand how threads or coroutines work as much as I should either but again, I have done a lot of research on my own and am now asking for outside help.

Here is the code I have. (I have censored potentially private information such as the discord channel ID)


import time
import requests
import re
import discord
import threading
import random
import asyncio
from discord.ext import tasks


bbUrlList = []
neUrlList = []
neGpuList = []
neSkipList = {}

client = discord.Client()

with open("DiscordToken.txt",'r') as tokenFile:
    token = str(tokenFile.readline()).replace("Token=","")

with open("bb.txt",'r') as file:
        for line in file:
            bbUrlList.append(line.split(" ")[1])

with open("ne.txt",'r') as file:
        for line in file:
            neUrlList.append(line.split(" ")[1])
            neGpuList.append(line.split(" ")[0])

async def bB(channel):
    while True:
        for x in range(0,len(bbUrlList)):
            url = bbUrlList[x]
            headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'}
            request = requests.get(url, headers=headers)
            request.encoding = "utf-8"
            text = request.text
            if re.search("add to cart", text, re.IGNORECASE) or re.search("see details", text, re.IGNORECASE):
                await channel.send("Bb appears to be dropping NOW!")
                print("[POSSIBLE DROP] Link:", bbUrlList[x])
                time.sleep(1800)
            else:
                print("[OUT OF STOCK] Link:", bbUrlList[x])
                time.sleep(random.randint(1,3))

async def ne(channel):
    while True:
        for x in range(0,len(neUrlList)):
            url = neUrlList[x]
            gpu = neGpuList[x]
            headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'}
            request = requests.get(url, headers=headers)
            request.encoding = "utf-8"
            text = request.text
            if re.search("add to cart", text, re.IGNORECASE):
                if url in neSkipList:
                    print("[REPEAT STOCK] Link:", url, neSkipList[url], "Rotations left.")
                    neSkipList[url] -= 1
                    if neSkipList[url] <= 0:
                        neSkipList.pop(url)
                    time.sleep(random.randint(1,3))
                    continue
                await channel.send("There might be a {} at {}".format(neGpuList[x], neUrlList[x]))
                print("[POSSIBLE STOCK] Link:", neUrlList[x])
                neSkipList[url] = 2
                time.sleep(random.randint(1,3))
            else:
                print("[OUT OF STOCK] Link:", neUrlList[x])
                time.sleep(random.randint(1,3))
                
@client.event            
async def on_ready():
    channel = client.get_channel(################)
    threading.Thread(target=asyncio.run, args = (bB(channel),)).start()
    threading.Thread(target=asyncio.run, args = (ne(channel),)).start()

##Also tried the following
##asyncio.run_coroutine_threadsafe(ne(channel), #Something Here#)
##asyncio.run_coroutine_threadsafe(bB(channel), #Something Here#)



client.run(token)

link77709
  • 1
  • 1

1 Answers1

0

I believe that there are two primary reasons this isn't working as expected, the first being that the requests module was built for Synchronous IO and doesn't work properly with Async. [See here for more] You can try and replace your requests module with something like aiohttp.

Second, time.sleep() is blocking, asyncio.sleep() is non-blocking. In the context of an async loop, asyncio will tell just that specific thread to wait, while the entirety of your code will be allowed to continue.

In your code, this would be put together look something like:

import time
import re
import discord
import threading
import random
import asyncio
import aiohttp
from discord.ext import tasks


bbUrlList = []
neUrlList = []
neGpuList = []
neSkipList = {}

client = discord.Client()

with open("DiscordToken.txt",'r') as tokenFile:
    token = str(tokenFile.readline()).replace("Token=","")

with open("bb.txt",'r') as file:
        for line in file:
            bbUrlList.append(line.split(" ")[1])

with open("ne.txt",'r') as file:
        for line in file:
            neUrlList.append(line.split(" ")[1])
            neGpuList.append(line.split(" ")[0])

async def bB(channel):
    async with aiohttp.ClientSession() as session:
        while True:
            for x in range(0,len(bbUrlList)):
                url = bbUrlList[x]
                headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'}
                async with session.get(url, headers=headers) as request:
                    text = await request.text()
                    if re.search("add to cart", text, re.IGNORECASE) or re.search("see details", text, re.IGNORECASE):
                        await channel.send("Bb appears to be dropping NOW!")
                        print("[POSSIBLE DROP] Link:", bbUrlList[x])
                        await asyncio.sleep(1800)
                    else:
                        print("[OUT OF STOCK] Link:", bbUrlList[x])
                        await asyncio.sleep(random.randint(1,3))

async def ne(channel):
    async with aiohttp.ClientSession() as session:
        while True:
            for x in range(0,len(neUrlList)):
                url = neUrlList[x]
                gpu = neGpuList[x]
                headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'}
                async with session.get(url, headers=headers) as request:
                    text = await request.text()
                    if re.search("add to cart", text, re.IGNORECASE):
                        if url in neSkipList:
                            print("[REPEAT STOCK] Link:", url, neSkipList[url], "Rotations left.")
                            neSkipList[url] -= 1
                            if neSkipList[url] <= 0:
                                neSkipList.pop(url)
                            await asyncio.sleep(random.randint(1,3))
                            continue
                        await channel.send("There might be a {} at {}".format(neGpuList[x], neUrlList[x]))
                        print("[POSSIBLE STOCK] Link:", neUrlList[x])
                        neSkipList[url] = 2
                        await asyncio.sleep(random.randint(1,3))
                    else:
                        print("[OUT OF STOCK] Link:", neUrlList[x])
                        await asyncio.sleep(random.randint(1,3))
                
@client.event            
async def on_ready():
    channel = client.get_channel(################)
    threading.Thread(target=asyncio.run, args = (bB(channel),)).start()
    threading.Thread(target=asyncio.run, args = (ne(channel),)).start()


client.run(token)

I don't have a ton of experience using aiohttp, so be sure to check out its documentation in order to tailor it to your specific needs.

Good luck with your project!

Eric
  • 31
  • 1
  • 7