Compare commits

...

19 Commits

Author SHA1 Message Date
44b3fda6de youtube: add support for /shorts/ URLs 2025-04-14 19:27:15 +00:00
f80c89ba09 youtube: add support for /live/ URLs 2025-04-08 09:02:17 +00:00
132d6a19ca Revert "useless: add vampir trigger"
This reverts commit ade97db922.

Link is down, so remove the trigger.
2025-02-27 13:55:14 +00:00
27c537b15a coins: fix 24h change 2025-02-25 21:08:16 +00:00
6a9bf7456b requirements: update lxml to 5.3 2025-02-04 12:46:12 +00:00
0edbc7a0f1 mcmaniac: fix a typo 2025-01-23 23:14:34 +00:00
360fd4d164 coins: display currency name in upper case 2024-12-17 00:17:38 +00:00
91abf2bf93 coins: switch to coingecko api
fix #19
2024-12-11 00:08:54 +00:00
3f7d1ae13c youtube: limit search to 1 video result 2024-06-02 15:31:24 +00:00
586a109a16 drugs, urlinfo, youtube: fix 'invalid escape sequence' SyntaxWarnings 2024-06-02 15:30:20 +00:00
6986798245 ascii: remove buddha command 2024-04-17 10:14:39 +00:00
b1100db649 urlinfo: make .* in front of the URL non-greedy
This is done because URLs may contain one or more other URLs that match
the url pattern (e.g. https://web.archive.org links). Because .* is
greedy by default, this caused only the last matching URL to be
captured, instead of the full URL.
2023-08-05 13:42:46 +00:00
1149417b56 quotes: replace non-functional print() with self.log.error() 2023-07-25 22:28:08 +00:00
f50fe88a14 linux: handle JSONDecodeError 2023-07-21 11:47:54 +00:00
fa51899a24 urlinfo: catch superclass RequestException instead of each exception individually 2023-07-21 11:44:20 +00:00
d59e170e8b linux: use json kernel.org release feeed
Reading the releases from the json feed is much simpler and it allows us
to drop the dependency on feedparser.
2022-11-08 00:25:05 +00:00
cb1cc4439a chore: update feedparser
The old feedparser 5.x versions are incompatible with recent setuptools
versions and can't be installed anymore
2022-11-07 20:24:26 +00:00
ffa64f509c linux: fix two SyntaxWarnings 2022-11-07 20:20:11 +00:00
9ba11a50cd youtube: fix a SyntaxWarning 2022-09-17 10:44:00 +00:00
10 changed files with 86 additions and 97 deletions

View File

@ -9,33 +9,6 @@ from . import Plugin
class Ascii(Plugin):
@command
def buddha(self, mask: IrcString, target: IrcString, args: Dict):
"""prints a giant swastika
%%buddha
"""
for line in (
'░░░░░░░░░░░░░░░▄▀▄░░░░░░░░░░░░░░░',
'░░░░░░░░░░░░░▄▀░░░▀▄░░░░░░░░░░░░░',
'░░░░░░░░░░░▄▀░░░░▄▀█░░░░░░░░░░░░░',
'░░░░░░░░░▄▀░░░░▄▀░▄▀░▄▀▄░░░░░░░░░',
'░░░░░░░▄▀░░░░▄▀░▄▀░▄▀░░░▀▄░░░░░░░',
'░░░░░░░█▀▄░░░░▀█░▄▀░░░░░░░▀▄░░░░░',
'░░░▄▀▄░▀▄░▀▄░░░░▀░░░░▄█▄░░░░▀▄░░░',
'░▄▀░░░▀▄░▀▄░▀▄░░░░░▄▀░█░▀▄░░░░▀▄░',
'░█▀▄░░░░▀▄░█▀░░░░░░░▀█░▀▄░▀▄░▄▀█░',
'░▀▄░▀▄░░░░▀░░░░▄█▄░░░░▀▄░▀▄░█░▄▀░',
'░░░▀▄░▀▄░░░░░▄▀░█░▀▄░░░░▀▄░▀█▀░░░',
'░░░░░▀▄░▀▄░▄▀░▄▀░█▀░░░░▄▀█░░░░░░░',
'░░░░░░░▀▄░█░▄▀░▄▀░░░░▄▀░▄▀░░░░░░░',
'░░░░░░░░░▀█▀░▄▀░░░░▄▀░▄▀░░░░░░░░░',
'░░░░░░░░░░░░░█▀▄░▄▀░▄▀░░░░░░░░░░░',
'░░░░░░░░░░░░░▀▄░█░▄▀░░░░░░░░░░░░░',
'░░░░░░░░░░░░░░░▀█▀░░░░░░░░░░░░░░░'
):
self.bot.privmsg(target, line)
@command
def rotschwuchtel(self, mask: IrcString, target: IrcString, args: Dict):
"""prints a giant hammer and sickle

View File

@ -10,12 +10,12 @@ def _currency(name, prefix=None, suffix=None):
def tostr(value):
return '{prefix}{value:,.2f}{suffix}'.format(
prefix=prefix or '',
suffix=suffix or ('' if (prefix or suffix) else (' ' + name)),
suffix=suffix or ('' if (prefix or suffix) else (' ' + name.upper())),
value=value)
return tostr
class Coins(Plugin):
API_URL = 'https://api.cryptowat.ch/markets/{market}/{crypto}{currency}/summary'
API_URL = 'https://api.coingecko.com/api/v3/simple/price'
CURRENCIES = {
'usd': _currency('usd', prefix='$'),
@ -23,60 +23,89 @@ class Coins(Plugin):
'eth': _currency('eth', suffix='Ξ'),
'btc': _currency('btc', suffix='฿'),
'xmr': _currency('xmr'),
'xrp': _currency('xrp')
}
CRYPTO_IDS = {
'btc': 'bitcoin',
'eth': 'ethereum',
'xmr': 'monero',
'xrp': 'ripple'
}
@command
def btc(self, mask: IrcString, target: IrcString, args: Dict):
"""Gets the Bitcoin values from cryptowatch.
"""Gets the Bitcoin values from coingecko.
%%btc [<currency>]
"""
return self.cryptowat_summary('btc', 'coinbase', args)
return self.coingecko_summary('btc', args)
@command
def eth(self, mask: IrcString, target: IrcString, args: Dict):
"""Gets the Ethereum values from cryptowatch.
"""Gets the Ethereum values from coingecko.
%%eth [<currency>]
"""
return self.cryptowat_summary('eth', 'coinbase', args)
return self.coingecko_summary('eth', args)
@command
def xmr(self, mask: IrcString, target: IrcString, args: Dict):
"""Gets the Monero values from cryptowatch.
"""Gets the Monero values from coingecko.
%%xmr [<currency>]
"""
return self.cryptowat_summary('xmr', 'bitfinex', args)
return self.coingecko_summary('xmr', args)
@command
def xrp(self, mask: IrcString, target: IrcString, args: Dict):
"""Gets the Ripple values from coingecko.
%%xrp [<currency>]
"""
return self.coingecko_summary('xrp', args)
def coingecko_summary(self, crypto: str, args: Dict):
crypto_id = self.CRYPTO_IDS.get(crypto)
if crypto_id is None:
raise ValueError
def cryptowat_summary(self, crypto: str, market: str, args: Dict):
currency = args.get('<currency>', 'eur').lower()
def format_response(response: str) -> str:
return f'\x02[{crypto.upper()}]\x02 {response}'
if currency not in self.CURRENCIES or crypto == currency:
return '\x02[{}]\x02 Can\'t convert or invalid currency: {}'.format(crypto.upper(), currency)
return format_response(f'Can\'t convert or invalid currency: {currency}')
def get_data(cur):
return requests.get(self.API_URL.format(crypto=crypto, market=market, currency=cur))
res = requests.get(self.API_URL, params={
'ids': crypto_id,
'vs_currencies': currency,
'include_market_cap': 'true',
'include_24hr_vol': 'true',
'include_24hr_change': 'true'
})
data = get_data(currency)
if not data and currency != 'usd':
data = get_data('usd')
currency = 'usd'
if not data:
return '\x02[{}]\x02 No data received'.format(crypto)
if res.status_code == 429:
return format_response('Rate Limit exceeded')
if res.status_code != 200:
return format_response('API Error')
data = res.json()[crypto_id]
if currency not in data:
return format_response('No data received')
to_currency = self.CURRENCIES[currency]
result = data.json()['result']
return '\x02[{crypto}]\x02 ' \
'Current: \x02\x0307{last}\x0F - ' \
'High: \x02\x0303{high}\x0F - ' \
'Low: \x02\x0304{low}\x0F - ' \
return format_response(
'Price: \x02\x0307{price}\x0F - ' \
'Change: \x02\x0307{change:,.2f}%\x0F - ' \
'Volume: \x02\x0307{volume}\x0F' \
'Volume: \x02\x0307{volume}\x0F - ' \
'Market Cap: \x02\x0307{market_cap}\x0F' \
.format(crypto=crypto.upper(),
last=to_currency(result['price']['last']),
high=to_currency(result['price']['high']),
low=to_currency(result['price']['low']),
change=result['price']['change']['percentage'] * 100,
volume=result['volume'])
price=to_currency(data[currency]),
change=data[f'{currency}_24h_change'],
volume=data[f'{currency}_24h_vol'],
market_cap=to_currency(data[f'{currency}_market_cap'])))

View File

@ -113,4 +113,4 @@ class Drugs(Plugin):
%%meth [<nick>]
"""
self.bot.action(target, 'legt {} eine dicke Line Meth \________'.format(args.get('<nick>', mask.nick)))
self.bot.action(target, r'legt {} eine dicke Line Meth \________'.format(args.get('<nick>', mask.nick)))

View File

@ -2,7 +2,7 @@
import random
import irc3
import feedparser
import requests
from docopt import Dict
from irc3.plugins.command import command
from irc3.utils import IrcString
@ -19,18 +19,18 @@ OS as defined by POSIX."""
class Linux(Plugin):
KERNEL_FEED = 'https://www.kernel.org/feeds/kdist.xml'
KERNEL_FEED = 'https://www.kernel.org/releases.json'
@irc3.event(r'(?i)^:\S+ PRIVMSG (?P<target>\S+) :(?:.* )?(debian|ubuntu|apt|dpkg|flash)(?: .*|$)')
def debillian(self, target: str):
"""Annoying RE trigger for debian with variable count of E."""
if random.randint(0, 3) is 0:
if random.randint(0, 3) == 0:
self.bot.privmsg(target, re_generator())
@irc3.event(r'(?i)^:\S+ PRIVMSG (?P<target>\S+) :.*(?<!gnu[/+])linux(?! kernel).*')
def linux(self, target: str):
"""Super annoying, useless 'Stallman is mad' trigger."""
if random.randint(0, 12) is 0:
if random.randint(0, 12) == 0:
self.bot.privmsg(target, GNU_LINUX)
@command
@ -39,21 +39,17 @@ class Linux(Plugin):
%%kernel
"""
feed = feedparser.parse(self.KERNEL_FEED)
# Cancel if no feed or entries
if not feed or not feed.get('entries'):
self.log.error('Error fetching kernel.org releases feed')
return
try:
res = requests.get(self.KERNEL_FEED, timeout=10).json()
except requests.exceptions.RequestException:
return '\x02[Kernel]\x02 Error fetching kernel.org releases'
except requests.exceptions.JSONDecodeError:
return '\x02[Kernel]\x02 Error decoding kernel.org releases'
# Make list of releases
releases = []
for e in feed['entries']:
version, branch = e['title'].split(': ')
if '(EOL)' in e['description']:
branch = '{}, \x1DEOL\x1D'.format(branch)
releases.append('\x02{}\x02 ({})'.format(version, branch))
for release in res['releases']:
releases.append('\x02{}\x02 ({}{})'.format(release['version'], release['moniker'],
', \x1DEOL\x1D' if release['iseol'] else ''))
return '\x02[Kernel]\x02 {}'.format(', '.join(releases))

View File

@ -66,4 +66,4 @@ class McManiac(DatabasePlugin):
result = cur.fetchone()
if result is None:
return
self.bot.privmsg(channel, f"{result['total']}. mcmanic added: {item}")
self.bot.privmsg(channel, f"{result['total']}. mcmaniac added: {item}")

View File

@ -87,7 +87,7 @@ class Quotes(DatabasePlugin):
self.con.commit()
except Error as ex:
# Rollback transaction on error
print(ex)
self.log.error(ex)
self.con.rollback()
else:
query = None

View File

@ -9,17 +9,17 @@ from . import Plugin
class URLInfo(Plugin):
BLACKLIST = [
"^https?:\/\/(?:www\.)?youtube\.com",
"^https?:\/\/youtu\.be",
"^https?:\/\/w0bm\.com",
"^https?:\/\/f0ck\.me",
"^https?:\/\/(?:(?:vid|img|thumb)\.)?pr0gramm\.com"
r"^https?:\/\/(?:www\.)?youtube\.com",
r"^https?:\/\/youtu\.be",
r"^https?:\/\/w0bm\.com",
r"^https?:\/\/f0ck\.me",
r"^https?:\/\/(?:(?:vid|img|thumb)\.)?pr0gramm\.com"
]
# set the size limit to 2 MB so we don't fully download too large resources
SIZE_LIMIT = 2 * 1024 ** 2
@irc3.event(r'(?i)^:\S+ PRIVMSG (?P<target>\S+) :.*(?P<url>https?:\/\/\S+\.\S+).*')
@irc3.event(r'(?i)^:\S+ PRIVMSG (?P<target>\S+) :.*?(?P<url>https?:\/\/\S+\.\S+).*')
def url_parser(self, target: str, url: str):
for regex in self.BLACKLIST:
if re.match(regex, url):
@ -39,7 +39,7 @@ class URLInfo(Plugin):
if size >= self.SIZE_LIMIT:
return
bytes_io.write(chunk)
except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError, requests.exceptions.ReadTimeout, requests.exceptions.TooManyRedirects):
except requests.exceptions.RequestException:
return
bytes_io.seek(0)

View File

@ -471,14 +471,6 @@ class Useless(DatabasePlugin):
nick = args.get('<nick>', mask.nick)
self.bot.action(target, 'hustet {0} in\'s Gesicht'.format(nick, nick))
@command
def vampir(self, mask: IrcString, target: IrcString, args: Dict):
"""Sends a url with the cute vampire cat :3
%%vampir
"""
return 'https://files.catbox.moe/ma2dfs.png'
@command
def hebamme(self, mask: IrcString, target: IrcString, args: Dict):
"""Tells you to get the fuck outta this group

View File

@ -15,7 +15,7 @@ from .utils import date_from_iso
class YouTube(Plugin):
URL = 'https://www.googleapis.com/youtube/v3'
API = '{}/videos?part=snippet,statistics,contentDetails'.format(URL)
SEARCH = '{}/search?part=id'.format(URL)
SEARCH = '{}/search?type=video&maxResults=1&part=id'.format(URL)
RETURN_YOUTUBE_DISLIKE_API = 'https://returnyoutubedislikeapi.com/votes'
def get_video_data(self, video_id: str):
@ -29,7 +29,7 @@ class YouTube(Plugin):
item = data['items'][0]
date = date_from_iso(item['snippet']['publishedAt'])
length = re.findall('(\d+[DHMS])', item['contentDetails']['duration'])
length = re.findall(r'(\d+[DHMS])', item['contentDetails']['duration'])
views = int(item['statistics'].get('viewCount', 0))
likes = int(item['statistics'].get('likeCount', 0))
@ -53,7 +53,7 @@ class YouTube(Plugin):
views=views,
date=date.strftime('%d.%m.%Y %H:%M:%S UTC'))
@irc3.event(r'(?i)^:\S+ PRIVMSG (?P<target>\S+) :.*(?:youtube.*?(?:v=|/v/)'
@irc3.event(r'(?i)^:\S+ PRIVMSG (?P<target>\S+) :.*(?:youtube.*?(?:v=|/v/|/live/|/shorts/)'
r'|youtu\.be/)(?P<video_id>[-_a-zA-Z0-9]+).*')
def youtube_parser(self, target: str, video_id: str):
data = self.get_video_data(video_id)
@ -70,7 +70,7 @@ class YouTube(Plugin):
if 'error' in data:
return '\x02[YouTube]\x02 Error performing search'
elif data['pageInfo']['totalResults'] is 0:
elif data['pageInfo']['totalResults'] == 0:
return '\x02[YouTube]\x02 No results found'
else:
video_id = data['items'][0]['id']['videoId']

View File

@ -6,6 +6,5 @@ aiocron~=1.3
psycopg2~=2.8
requests~=2.22
GitPython~=3.0
feedparser~=5.2
python-dotenv~=0.17.1
lxml~=4.6
lxml~=5.3