Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dpy 1.7 #39

Merged
merged 22 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cb2c142
Updates for discord.py 1.5.0 update. This brings in some changes to t…
Oct 18, 2020
64f51ed
Fix for #32 - uses the available info on the user and role to call in…
Oct 18, 2020
c7f76f9
Fixed missing parameter name to set_image
Oct 19, 2020
6420c83
Resolved issue with adding offline streams due not agetstreamoffline …
Oct 20, 2020
f8b5ff2
Fixed issue with debug purge not checking for correct number of argum…
Oct 20, 2020
1ce96ed
Deleted announcement messages are now removed from the cache and Save…
Oct 24, 2020
9ed1521
Corrected issue in picartorecord that did not remove the record from …
Oct 31, 2020
c177029
Fixed missing character in help messages.
Oct 31, 2020
2104638
Fixed issues with new API down messages.
Oct 31, 2020
44ea05c
Fixed help still showing 'listen' command - was changed to 'channel'.…
Nov 1, 2020
c561295
Removed snowflake parameter from simpembed and makeembed, changed to …
Nov 1, 2020
fb76e2d
Updated version number.
Nov 1, 2020
23ec620
savedata no longer discards records for unused streams. Since we reco…
Nov 1, 2020
d0fe6ea
Made message parameter in setmsgid required. The default None paramet…
Nov 4, 2020
aa7591f
resolveuser doesn't work without intents, meaning anything using it w…
Mar 19, 2021
89a1172
Update Picarto API URLs for new API version.
Apr 8, 2021
4bc46a0
Fix picarto date format change.
Apr 9, 2021
5e61dc2
Added command to set the bot status which is preserved across reconne…
Apr 10, 2021
b6232ca
Handler responses for API contexts are now a reply to the message tha…
Apr 12, 2021
7e1e3ab
Resolves #37
Apr 12, 2021
579a3f9
Fix for Picarto API changes - avatar URL is always provided, and is n…
Apr 29, 2021
56dc074
Fix error in description of loaddata
Dec 19, 2021
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,6 @@ venv.bak/
# token storage
/apitoken.py
/dbcontexts.bin

# PyCharm files
/.idea
326 changes: 195 additions & 131 deletions basecontext.py

Large diffs are not rendered by default.

286 changes: 178 additions & 108 deletions dbcontext.py

Large diffs are not rendered by default.

277 changes: 140 additions & 137 deletions picartoclass.py
Original file line number Diff line number Diff line change
@@ -1,137 +1,140 @@
# context to monitor picarto streams

import json # Interpreting json results - updateparsed most likely needs this.
import discord # The discord API module. Most useful for making Embeds
# import asyncio # Use this for sleep, not time.sleep.
import datetime # Stream durations, time online, etc.
import basecontext # Base class for our API based context
import urllib.request as request # Send HTTP requests - debug use only NOT IN BOT

parsed = {} # Dict with key == 'name'
lastupdate = basecontext.Updated() # Class that tracks if update succeeded - empty if not successful


# Old non-async method. Kept for debugging.
def connect():
global parsed
newrequest = request.Request("https://api.picarto.tv/v1/online?adult=true&gaming=true")
newconn = request.urlopen(newrequest)
buff = newconn.read()
parsed = {item['name']: item for item in json.loads(buff)}
return True


# Gets the detailed information about a stream. Non-async, debugging.
def getstream(recordid):
try:
newrequest = request.Request("https://api.picarto.tv/v1/channel/name/" + recordid)
newconn = request.urlopen(newrequest)
buff = newconn.read()
parse = json.loads(buff)
return parse
except (KeyError, Exception):
return False


class PicartoRecord(basecontext.StreamRecord):

__slots__ = []

values = ['adult', 'gaming', 'name', 'title', 'viewers']
# Keys that are present in a Picarto stream. We don't currently need most of these.
# values2 = ['user_id', 'name', 'avatar', 'online', 'viewers', 'viewers_total', 'thumbnails', 'followers',
# 'subscribers', 'adult', 'category', 'account_type', 'commissions', 'recordings', 'title',
# 'description_panels', 'private', 'private_message', 'gaming', 'chat_settings', 'last_live', 'tags',
# 'multistream', 'languages', 'following']
# The first item in multistream is the host.

streamurl = "https://picarto.tv/{0}" # URL for going to watch the stream

def __init__(self, recdict, detailed=False):
super().__init__(recdict, detailed)
self.preview = recdict['thumbnails']['web']
if detailed:
if recdict['multistream']:
self.multistream = [basecontext.MultiClass(x['adult'], x['name'], x['user_id'])
for x in recdict['multistream']
if (x['online'] and x['user_id'] != self.name)]
else:
self.multistream = []
self.online = recdict['online']
self.time = datetime.datetime.strptime(''.join(recdict['last_live'].rsplit(':', 1)), '%Y-%m-%dT%H:%M:%S%z')
self.avatar = recdict['avatar'] # We COULD make this ourself same as below, but easier to just grab it.
self.viewers_total = recdict['viewers_total']
else:
# Non detailed records omit the time and avatar URLs, but we can make those easily enough.
self.time = datetime.datetime.now(datetime.timezone.utc)
self.avatar = "https://picarto.tv/user_data/usrimg/" + recdict['name'].lower() + "/dsdefault.jpg "
self.multistream = recdict['multistream']
self.online = True

async def detailembed(self, showprev=True):
"""This generates the embed to send when detailed info about a stream is requested. More information is provided
than with the other embeds.

:type showprev: bool
:rtype: discord.Embed
:param showprev: Should the embed include the preview image?
:return: a discord.Embed representing the current stream.
"""
description = self.title
multstring = ""
# If the stream is in a multi, we need to assemble the string that says
# who they are multistreaming with.
if self.ismulti:
online = self.otherstreams
if online:
multstring += " and streaming with " + online[0].name
if len(online) == 2:
multstring += " and " + online[1].name
elif len(online) == 3:
multstring += ", " + online[1].name + ", and " + online[2].name
# print(multstring," : ", str(record['multistream']))
myembed = discord.Embed(
title=self.name + "'s stream is " + ("" if self.online else "not ") + "online" + multstring,
url="https://picarto.tv/" + self.name, description=description)
myembed.add_field(name="Adult: " + ("Yes" if self.adult else "No"),
value="Gaming: " + ("Yes" if self.gaming else "No"), inline=True)
if self.online:
lastonline = await self.streammsg(None)
viewers = "Viewers: " + str(self.viewers)
else:
# If this were used on a non-detailed record, it would be the time the record was created.
lastonline = "Last online: " + self.time.strftime("%m/%d/%Y")
viewers = "Total Views: " + str(self.total_views[1])
myembed.add_field(name=lastonline, value=viewers, inline=True)
if showprev:
myembed.set_image(url=self.preview_url)
myembed.set_thumbnail(url=self.avatar)
return myembed


class PicartoContext(basecontext.APIContext):
defaultname = "picarto" # This is used to name this context and is the command to call it. Must be unique.
streamurl = "https://picarto.tv/{0}" # URL for going to watch the stream
apiurl = "https://api.picarto.tv/v1/online?adult=true&gaming=true" # URL to call to find online streams
channelurl = "https://api.picarto.tv/v1/channel/name/{0}" # URL to call to get information on a single stream
recordclass = PicartoRecord

def __init__(self, instname=None):
# Init our base class
basecontext.APIContext.__init__(self, instname)
# Our parsed is going to be the global parsed in our module, instead of the
# basecontext parsed. This gets shared with ALL instances of this class.
# Primarily this will allow sharing API response data with all instances.
self.parsed = parsed # Removing any of this isn't recommended.
self.lastupdate = lastupdate # Tracks if last API update was successful.
# Adding stuff below here is fine, obviously.

async def getrecordid(self, record):
"""Gets the name of the record used to uniquely id the stream. Generally, record['name'] or possibly
record['id']. Used to store info about the stream, such as who is watching and track announcement messages.

:rtype: str
:param record: A full stream record as returned by the API.
:return: A string with the record's unique name.
"""
return record['name']
# context to monitor picarto streams

import json # Interpreting json results - updateparsed most likely needs this.
import discord # The discord API module. Most useful for making Embeds
# import asyncio # Use this for sleep, not time.sleep.
import datetime # Stream durations, time online, etc.
import basecontext # Base class for our API based context
import urllib.request as request # Send HTTP requests - debug use only NOT IN BOT

parsed = {} # Dict with key == 'name'
lastupdate = basecontext.Updated() # Class that tracks if update succeeded - empty if not successful


# Old non-async method. Kept for debugging.
def connect():
global parsed
newrequest = request.Request("https://api.picarto.tv/v1/online?adult=true&gaming=true")
newconn = request.urlopen(newrequest)
buff = newconn.read()
parsed = {item['name']: item for item in json.loads(buff)}
return True


# Gets the detailed information about a stream. Non-async, debugging.
def getstream(recordid):
try:
newrequest = request.Request("https://api.picarto.tv/v1/channel/name/" + recordid)
newconn = request.urlopen(newrequest)
buff = newconn.read()
parse = json.loads(buff)
return parse
except (KeyError, Exception):
return False


# TODO May want to make this replace Gaming with Private if Private is enabled. Happens rarely but useful if it does.
# 'private' was the key for this, is it still?
class PicartoRecord(basecontext.StreamRecord):

__slots__ = []

values = ['adult', 'gaming', 'name', 'title', 'viewers']
# Keys that are present in a Picarto stream. We don't currently need most of these.
# values2 = ['user_id', 'name', 'avatar', 'online', 'viewers', 'viewers_total', 'thumbnails', 'followers',
# 'subscribers', 'adult', 'category', 'account_type', 'commissions', 'recordings', 'title',
# 'description_panels', 'private', 'private_message', 'gaming', 'chat_settings', 'last_live', 'tags',
# 'multistream', 'languages', 'following']
# The first item in multistream is the host.

streamurl = "https://picarto.tv/{0}" # URL for going to watch the stream

def __init__(self, recdict, detailed=False):
super().__init__(recdict, detailed)
self.preview = recdict['thumbnails']['web']
self.avatar = recdict['avatar'] # This is now provided by the API always, which is good as it's not static
if detailed:
if recdict['multistream']:
self.multistream = [basecontext.MultiClass(x['adult'], x['name'], x['user_id'])
for x in recdict['multistream']
if (x['online'] and x['name'] != self.name)]
else:
self.multistream = []
self.online = recdict['online']
# TODO Sometimes time is None, presumably if they have never streamed. This fails here.
self.time = datetime.datetime.strptime(''.join(recdict['last_live']), '%Y-%m-%d %H:%M:%S')\
.replace(tzinfo=datetime.timezone.utc)
self.viewers_total = recdict['viewers_total']
else:
# Non detailed records omit the time and avatar URLs, but we can make those easily enough.
self.time = datetime.datetime.now(datetime.timezone.utc)
self.multistream = recdict['multistream']
self.online = True

async def detailembed(self, showprev=True):
"""This generates the embed to send when detailed info about a stream is requested. More information is provided
than with the other embeds.

:type showprev: bool
:rtype: discord.Embed
:param showprev: Should the embed include the preview image? Generally yes unless it's hidden by adult options.
:return: a discord.Embed representing the current stream.
"""
description = self.title
multstring = ""
# If the stream is in a multi, we need to assemble the string that says
# who they are multistreaming with.
if self.ismulti:
online = self.otherstreams
if online:
multstring += " and streaming with " + online[0].name
if len(online) == 2:
multstring += " and " + online[1].name
elif len(online) == 3:
multstring += ", " + online[1].name + ", and " + online[2].name
# print(multstring," : ", str(record['multistream']))
myembed = discord.Embed(
title=self.name + "'s stream is " + ("" if self.online else "not ") + "online" + multstring,
url="https://picarto.tv/" + self.name, description=description)
myembed.add_field(name="Adult: " + ("Yes" if self.adult else "No"),
value="Gaming: " + ("Yes" if self.gaming else "No"), inline=True)
if self.online:
lastonline = await self.streammsg(None)
viewers = "Viewers: " + str(self.viewers)
else:
# If this were used on a non-detailed record, it would be the time the record was created.
lastonline = "Last online: " + self.time.strftime("%m/%d/%Y")
viewers = "Total Views: " + str(self.total_views[1])
myembed.add_field(name=lastonline, value=viewers, inline=True)
if showprev:
myembed.set_image(url=self.preview_url)
myembed.set_thumbnail(url=self.avatar)
return myembed


class PicartoContext(basecontext.APIContext):
defaultname = "picarto" # This is used to name this context and is the command to call it. Must be unique.
streamurl = "https://picarto.tv/{0}" # URL for going to watch the stream
apiurl = "https://api.picarto.tv/api/v1/online?adult=true&gaming=true" # URL to call to find online streams
channelurl = "https://api.picarto.tv/api/v1/channel/name/{0}" # URL to call to get information on a single stream
recordclass = PicartoRecord

def __init__(self, instname=None):
# Init our base class
basecontext.APIContext.__init__(self, instname)
# Our parsed is going to be the global parsed in our module, instead of the
# basecontext parsed. This gets shared with ALL instances of this class.
# Primarily this will allow sharing API response data with all instances.
self.parsed = parsed # Removing any of this isn't recommended.
self.lastupdate = lastupdate # Tracks if last API update was successful.
# Adding stuff below here is fine, obviously.

async def getrecordid(self, record):
"""Gets the name of the record used to uniquely id the stream. Generally, record['name'] or possibly
record['id']. Used to store info about the stream, such as who is watching and track announcement messages.

:rtype: str
:param record: A full stream record as returned by the API.
:return: A string with the record's unique name.
"""
return record['name']
4 changes: 3 additions & 1 deletion piczelclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ async def detailembed(self, showprev=True):

:type showprev: bool
:rtype: discord.Embed
:param showprev: Should the embed include the preview image?
:param showprev: Should the embed include the preview image? Generally yes unless it's hidden by adult options.
:return: a discord.Embed representing the current stream.
"""
# This generates the embed to send when detailed info about a stream is
Expand Down Expand Up @@ -209,6 +209,8 @@ async def agetstream(self, recordid, headers=None):
record = False
return PiczelRecord(record, True)

agetstreamoffline = agetstream

async def getrecordid(self, record):
"""Gets the name of the record used to uniquely id the stream. Generally, record['name'] or possibly
record['id']. Used to store info about the stream, such as who is watching and track announcement messages.
Expand Down
Loading