Skip to content

Commit

Permalink
Initial threads implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
amadejkastelic committed Aug 22, 2024
1 parent d6ee183 commit cc7ac2a
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 10 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pymemcache = "==4.0.0"
"discord-oauth2.py" = "==1.2.1"
twitch-dl = "==2.3.1"
pydantic = "==2.8.2"
threads-net = {git = "https://github.com/Jxck-S/threads-net-freedom.git"}

[dev-packages]
black = "==24.8.0"
Expand Down
60 changes: 51 additions & 9 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Integration(enum.Enum):
FACEBOOK = 'facebook'
TIKTOK = 'tiktok'
REDDIT = 'reddit'
THREADS = 'threads'
TWITCH = 'twitch'
TWITTER = 'twitter'
YOUTUBE = 'youtube'
2 changes: 2 additions & 0 deletions bot/downloader/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from bot.downloader.facebook import client as facebook_client
from bot.downloader.instagram import client as instagram_client
from bot.downloader.reddit import client as reddit_client
from bot.downloader.threads import client as threads_client
from bot.downloader.tiktok import client as tiktok_client
from bot.downloader.twitch import client as twitch_client
from bot.downloader.twitter import client as twitter_client
Expand All @@ -14,6 +15,7 @@
facebook_client.FacebookClientSingleton,
instagram_client.InstagramClientSingleton,
reddit_client.RedditClientSingleton,
threads_client.ThreadsClientSingleton,
tiktok_client.TiktokClientSingleton,
twitch_client.TwitchClientSingleton,
twitter_client.TwitterClientSingleton,
Expand Down
Empty file.
103 changes: 103 additions & 0 deletions bot/downloader/threads/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import json
import re
import typing

import requests
import threads
from django.conf import settings

from bot import constants
from bot import domain
from bot import logger
from bot.downloader import base
from bot.downloader.threads import config


class ThreadsClientSingleton(base.BaseClientSingleton):
DOMAINS = ['threads.net']
_CONFIG_SCHEMA = config.ThreadsConfig

@classmethod
def _create_instance(cls) -> None:
conf: config.ThreadsConfig = cls._load_config(conf=settings.INTEGRATION_CONFIGURATION.get('threads', {}))

if not conf.enabled:
logger.info('Threads integration not enabled')
cls._INSTANCE = base.MISSING
return

cls._INSTANCE = ThreadsClient()


class ThreadsClient(base.BaseClient):
INTEGRATION = constants.Integration.THREADS

def __init__(self) -> None:
super().__init__()
self.client = threads.Threads()

async def get_integration_data(self, url: str) -> typing.Tuple[constants.Integration, str, typing.Optional[int]]:
return self.INTEGRATION, url.strip('/').split('?')[0].split('/')[-1], None

async def get_post(self, url: str) -> domain.Post:
_, url_id, _ = await self.get_integration_data(url)

thread = self._get_thread(url_id)
logger.debug('Obtained thread', thread=thread)

def _get_thread_id(self, url_id: str):
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'

thread_id = 0

for letter in url_id:
thread_id = (thread_id * 64) + alphabet.index(letter)

return thread_id

def _get_thread(self, url_id: str) -> dict:
thread_id = self._get_thread_id(url_id)
api_token = self._get_threads_api_token()

headers = {
'Authority': 'www.threads.net',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.9',
'Cache-Control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'https://www.threads.net',
'Pragma': 'no-cache',
'Sec-Fetch-Site': 'same-origin',
'X-ASBD-ID': '129477',
'X-FB-LSD': api_token,
'X-IG-App-ID': '238260118697367',
'X-FB-Friendly-Name': 'BarcelonaPostPageQuery',
}

response = requests.post(
url=self.THREADS_API_URL,
headers=headers,
data={
'lsd': api_token,
'variables': json.dumps(
{
'postID': thread_id,
},
),
'doc_id': '5587632691339264',
},
)

return response.json()

def _get_threads_api_token(self) -> str:
response = requests.get(
url='https://www.instagram.com/instagram',
headers=self.fetch_html_headers,
)

token_key_value = re.search('LSD",\\[\\],{"token":"(.*?)"},\\d+\\]', response.text).group()
token_key_value = token_key_value.replace('LSD",[],{"token":"', '')
token = token_key_value.split('"')[0]

return token
7 changes: 7 additions & 0 deletions bot/downloader/threads/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from bot.downloader import base


class ThreadsConfig(base.BaseClientConfig):
"""
No additional settings for Threads integration
"""
6 changes: 6 additions & 0 deletions bot/integrations/discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ async def on_message(self, message: discord.Message): # noqa: C901
return

if service.should_handle_url(url) is False:
logger.debug(
'Handling for URL not implemented',
url=url,
server_uid=str(message.guild.id),
server_vendor=self.VENDOR.value,
)
return

new_message = (await asyncio.gather(message.delete(), message.channel.send('🔥 Working on it 🥵')))[1]
Expand Down
1 change: 0 additions & 1 deletion shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ pkgs.mkShell {
python312
python312Packages.pip
python312Packages.python-magic
python312Packages.playwright
playwright
playwright-driver.browsers
docker-compose
Expand Down

0 comments on commit cc7ac2a

Please sign in to comment.