#----- Getting forum topics and posts
import json
import requests
from . import user
from . import exceptions
from .commons import api_iterative_data, api_iterative_simple, headers
[docs]
class ForumTopic:
'''
Represents a Scratch forum topic.
Attributes:
:.title:
:.category:
:.closed:
:.deleted:
:.post_count:
:.update(): Updates the attributes
'''
def __init__(self, **entries):
self.__dict__.update(entries)
if not hasattr(self, "_session"):
self._session = None
if self._session is None:
self._headers = headers
self._cookies = {}
else:
self._headers = self._session._headers
self._cookies = self._session._cookies
try:
self._headers.pop("Cookie")
except Exception: pass
[docs]
def update(self):
"""
Updates the attributes of the ForumTopic object
"""
topic = requests.get(f"https://scratchdb.lefty.one/v3/forum/topic/info/{self.id}").json()
return self._update_from_dict(topic)
def _update_from_dict(self, topic):
self.title = topic["title"]
self.category = topic["category"]
if topic["closed"] == 1:
self.closed = True
else:
self.closed = False
if topic["deleted"] == 1:
self.deleted = True
else:
self.deleted = False
self.post_count = topic["post_count"]
[docs]
def activity(self):
"""
Returns:
list<dict>: A list that contains the history of the forum topic (changes of topic title etc.)
"""
return requests.get(f"https://scratchdb.lefty.one/v3/forum/topic/history/{self.id}").json()
[docs]
def posts(self, *, page=0, order="oldest"):
"""
Args:
page (int): The page of the forum topic that should be returned.
order (str): Specifies the order of the returned posts. "newest" means the first returned post is the newest one, "oldest" means it is the oldest one.
Returns:
list<scratchattach.forum.ForumPost>: A list containing the posts from the specified page of the forum topic
"""
data = requests.get(f"https://scratchdb.lefty.one/v3/forum/topic/posts/{self.id}/{page}?o={order}").json()
return_data = []
for o in data:
a = ForumPost(id = o["id"], _session = self._session)
a._update_from_dict(o)
return_data.append(a)
return return_data
[docs]
def first_post(self):
"""
Returns:
scratchattach.forum.ForumPost: An object representing the first topic post
"""
o = requests.get(f"https://scratchdb.lefty.one/v3/forum/topic/posts/{self.id}/0?o=oldest").json()[0]
a = ForumPost(id = o["id"], _session = self._session)
a._update_from_dict(o)
return a
[docs]
class ForumPost:
'''
Represents a Scratch forum post.
Attributes:
:.id:
:.author: The name of the user who created this post
:.posted: The date the post was made
:.edited: The date of the most recent post edit. If the post wasn't edited this is None
:.edited_by: The name of the user who made the most recent edit. If the post wasn't edited this is None
:.deleted: Whether the post was deleted
:.html_content: Returns the content as HTML
:.bb_content: Returns the content as BBCode
:.topic_id: The id of the topic the post is in
:.topic_name: The name of the topic the post is in
:.topic_category: The name of the category the post topic is in
:.update(): Updates the attributes
'''
def __init__(self, **entries):
self.__dict__.update(entries)
if not hasattr(self, "_session"):
self._session = None
if self._session is None:
self._headers = headers
self._cookies = {}
else:
self._headers = self._session._headers
self._cookies = self._session._cookies
[docs]
def update(self):
"""
Updates the attributes of the ForumPost object
"""
post = requests.get(f"https://scratchdb.lefty.one/v3/forum/post/info/{self.id}").json()
return self._update_from_dict(post)
def _update_from_dict(self, post):
self.author = post["username"]
self.posted = post["time"]["posted"]
self.edited = post["time"]["edited"]
self.edited_by = post["editor"]
if post["deleted"] == 1:
self.deleted = True
else:
self.deleted = False
self.html_content = post["content"]["html"]
self.bb_content = post["content"]["bb"]
self.topic_id = post["topic"]["id"]
self.topic_name = post["topic"]["title"]
self.topic_category = post["topic"]["category"]
[docs]
def get_topic(self):
"""
Returns:
scratchattach.forum.ForumTopic: An object representing the forum topic this post is in.
"""
t = ForumTopic(id = self.topic_id, _session = self._session)
t.update()
return t
[docs]
def ocular_reactions(self):
return requests.get(f"https://my-ocular.jeffalo.net/api/reactions/{self.id}").json()
[docs]
def get_author(self):
"""
Returns:
scratchattach.user.User: An object representing the user who created this forum post.
"""
u = user.User(username=self.author, _session = self._session)
u.update()
return u
[docs]
def edit(self, new_content):
"""
Changes the content of the forum post. You can only use this function if this object was created using :meth:`scratchattach.session.Session.connect_post` or through another method that requires authentication. You must own the forum post.
Args:
new_content (str): The text that the forum post will be set to.
"""
cookies = self._cookies
cookies["accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
cookies["Content-Type"] = "application/x-www-form-urlencoded"
r = requests.post(
f"https://scratch.mit.edu/discuss/post/{self.id}/edit/",
headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-language": "de,en;q=0.9",
"cache-control": "max-age=0",
"content-type": "application/x-www-form-urlencoded",
"sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Google Chrome\";v=\"101\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"Referer": f"https://scratch.mit.edu/discuss/post/{self.id}/edit/",
"x-csrftoken": "a"
},
cookies = cookies,
json = f"csrfmiddlewaretoken=a&body={new_content}&"
)
[docs]
def get_topic(topic_id):
"""
Gets a forum topic without logging in.
Args:
topic_id (int): ID of the requested forum topic
Returns:
scratchattach.forum.ForumTopic: An object representing the requested forum topic
Warning:
Any methods that require authentication will not work on the returned object.
If you want to use methods that require authentication, create the object with :meth:`scratchattach.session.Session.connect_topic` instead.
"""
try:
topic = ForumTopic(id=int(topic_id))
topic.update()
return topic
except KeyError:
return None
[docs]
def get_topic_list(category_name, *, page=0, include_deleted=False):
"""
Gets the topics from a forum category without logging in. Data fetched from ScratchDB.
Args:
category_name (str): Name of the forum category
Keyword Arguments:
page (str): Page of the category topics that should be returned
include_deleted (boolean): Whether deleted topics should be returned too
Returns:
list<scratchattach.forum.ForumTopic>: A list containing the forum topics from the specified category
Warning:
Any methods that require authentication will not work on the returned objects.
If you want to use methods that require authentication, get the forum topics with :meth:`scratchattach.session.Session.connect_topic_list` instead.
"""
category_name = category_name.replace(" ", "%20")
if include_deleted:
filter = 0
else:
filter = 1
try:
data = requests.get(f"https://scratchdb.lefty.one/v3/forum/category/topics/{category_name}/{page}?detail=1&filter={filter}").json()
return_data = []
for topic in data:
t = ForumTopic(id = topic["id"])
t._update_from_dict(topic)
return_data.append(t)
return return_data
except Exception:
return None
[docs]
def get_post(post_id):
"""
Gets a forum post without logging in. Data fetched from ScratchDB.
Args:
post_id (int): ID of the requested forum post
Returns:
scratchattach.forum.ForumPost: An object that represents the requested forum post
Warning:
Any methods that require authentication (like post.edit) will not work on the returned object.
If you want to use these methods, get the forum post with :meth:`scratchattach.session.Session.connect_post` instead.
"""
try:
post = ForumPost(id=int(post_id))
post.update()
return post
except KeyError:
return None