Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.8.18
1.8.19
143 changes: 52 additions & 91 deletions src/torchlight/Commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from re import Match, Pattern
from typing import Any, cast

import aiohttp
import defusedxml.ElementTree as etree
import geoip2.database
import gtts
Expand All @@ -34,6 +33,7 @@
get_url_youtube_info,
print_url_metadata,
)
from torchlight.Utils import Utils


class BaseCommand:
Expand Down Expand Up @@ -295,20 +295,13 @@ def Clean(self, text: str) -> str:
).strip()

async def Calculate(self, parameters_json: dict[str, str], player: Player) -> int:
async with aiohttp.ClientSession() as session:
resp = await asyncio.wait_for(
session.get(
"http://api.wolframalpha.com/v2/query",
params=parameters_json,
),
10,
)
if not resp:
return 1

data = await asyncio.wait_for(resp.text(), 5)
if not data:
return 2
data = await Utils.FetchText(
"http://api.wolframalpha.com/v2/query",
params=parameters_json,
timeout=15,
)
if not data:
return 2

root = etree.fromstring(data)

Expand Down Expand Up @@ -389,29 +382,21 @@ async def _func(self, message: list[str], player: Player) -> int:
if self.check_disabled(player):
return -1

async with aiohttp.ClientSession() as session:
resp = await asyncio.wait_for(
session.get(f"https://api.urbandictionary.com/v0/define?term={message[1]}"),
5,
)
if not resp:
return 1

data = await asyncio.wait_for(resp.json(), 5)
if not data:
return 3
data = await Utils.FetchJson(f"https://api.urbandictionary.com/v0/define?term={message[1]}")
if not data:
return 3

if "list" not in data or not data["list"]:
self.torchlight.SayChat(f"[UB] No definition found for: {message[1]}", player)
return 4
if "list" not in data or not data["list"]:
self.torchlight.SayChat(f"[UB] No definition found for: {message[1]}", player)
return 4

def print_item(item: dict[str, Any]) -> None:
self.torchlight.SayChat(
"[UD] {word} ({thumbs_up}/{thumbs_down}): {definition}\n{example}".format(**item),
player,
)
def print_item(item: dict[str, Any]) -> None:
self.torchlight.SayChat(
"[UD] {word} ({thumbs_up}/{thumbs_down}): {definition}\n{example}".format(**item),
player,
)

print_item(data["list"][0])
print_item(data["list"][0])

return 0

Expand Down Expand Up @@ -456,21 +441,13 @@ async def _func(self, message: list[str], player: Player) -> int:
else:
search = f"q={message[1]}"

async with aiohttp.ClientSession() as session:
resp = await asyncio.wait_for(
session.get(
"https://api.openweathermap.org/data/2.5/weather?APPID={}&units=metric&{}".format(
self.torchlight.config["OpenWeatherAPIKey"], search
)
),
5,
data = await Utils.FetchJson(
"https://api.openweathermap.org/data/2.5/weather?APPID={}&units=metric&{}".format(
self.torchlight.config["OpenWeatherAPIKey"], search
)
if not resp:
return 2

data = await asyncio.wait_for(resp.json(), 5)
if not data:
return 3
)
if not data:
return 3

if data["cod"] != 200:
self.torchlight.SayPrivate(player, "[OW] {}".format(data["message"]))
Expand Down Expand Up @@ -520,49 +497,8 @@ async def _func(self, message: list[str], player: Player) -> int:
search = "autoip"
additional = "?geo_ip={}".format(player.address.split(":")[0])
else:
async with aiohttp.ClientSession() as session:
resp = await asyncio.wait_for(
session.get(f"http://autocomplete.wunderground.com/aq?format=JSON&query={message[1]}"),
5,
)
if not resp:
return 2

try:
data = await asyncio.wait_for(resp.json(), 5)
if not data:
return 3
except Exception as e:
self.logger.error(e)
self.torchlight.SayPrivate(
message="Failed to retrieve data from the wunderground api",
player=player,
)
return 1

if not data["RESULTS"]:
self.torchlight.SayPrivate(player, "[WU] No cities match your search query.")
return 4

search = data["RESULTS"][0]["name"]
additional = ""

async with aiohttp.ClientSession() as session:
resp = await asyncio.wait_for(
session.get(
"http://api.wunderground.com/api/{}/conditions/q/{}.json{}".format(
self.torchlight.config["WundergroundAPIKey"],
search,
additional,
)
),
5,
)
if not resp:
return 2

try:
data = await asyncio.wait_for(resp.json(), 5)
data = await Utils.FetchJson(f"http://autocomplete.wunderground.com/aq?format=JSON&query={message[1]}")
if not data:
return 3
except Exception as e:
Expand All @@ -573,6 +509,31 @@ async def _func(self, message: list[str], player: Player) -> int:
)
return 1

if not data["RESULTS"]:
self.torchlight.SayPrivate(player, "[WU] No cities match your search query.")
return 4

search = data["RESULTS"][0]["name"]
additional = ""

try:
data = await Utils.FetchJson(
"http://api.wunderground.com/api/{}/conditions/q/{}.json{}".format(
self.torchlight.config["WundergroundAPIKey"],
search,
additional,
)
)
if not data:
return 3
except Exception as e:
self.logger.error(e)
self.torchlight.SayPrivate(
message="Failed to retrieve data from the wunderground api",
player=player,
)
return 1

if "error" in data["response"]:
self.torchlight.SayPrivate(
player,
Expand Down
9 changes: 3 additions & 6 deletions src/torchlight/URLInfo.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import asyncio
import io
import json
import logging
Expand All @@ -18,18 +17,16 @@

# @profile
async def get_url_data(url: str) -> tuple[bytes, str, int]:
async with aiohttp.ClientSession() as session:
resp = await asyncio.wait_for(session.get(url), 5)
if resp:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session:
async with session.get(url) as resp:
content_type: str = resp.headers.get("Content-Type", "")
content_length_raw: str = resp.headers.get("Content-Length", "")
content = await asyncio.wait_for(resp.content.read(65536), 5)
content = await resp.content.read(65536)

content_length = -1
if content_length_raw:
content_length = int(content_length_raw)

resp.close()
return content, content_type, content_length


Expand Down
15 changes: 15 additions & 0 deletions src/torchlight/Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,23 @@
from collections.abc import Coroutine
from typing import Any

import aiohttp


class Utils:
@staticmethod
async def FetchText(url: str, *, timeout: float = 10, params: dict[str, str] | None = None) -> str:
# timeout is the total budget for connecting and reading the body.
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session:
async with session.get(url, params=params) as resp:
return await resp.text()

@staticmethod
async def FetchJson(url: str, *, timeout: float = 10, params: dict[str, str] | None = None) -> Any:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session:
async with session.get(url, params=params) as resp:
return await resp.json()

@staticmethod
def FireAndForget(coro: Coroutine[Any, Any, Any], logger: logging.Logger) -> asyncio.Task:
# Logs exceptions instead of letting asyncio silently discard them on an untracked task.
Expand Down
Loading