public version & README.md
This commit is contained in:
parent
f6b401b527
commit
11a50ccdd9
14 changed files with 574 additions and 12 deletions
117
README.md
117
README.md
|
@ -1,3 +1,118 @@
|
|||
# masto-gitsocial-bridge
|
||||
|
||||
bridge between mastodon and git social
|
||||
bridge between mastodon and bcrypt's [git-social](https://github.com/diracdeltas/tweets)
|
||||
|
||||
## From git to masto
|
||||
|
||||
Post on git-social...
|
||||
|
||||
![Post on git social: This is what we call infrastructure shitposting](img/git-to-masto_0.png)
|
||||
|
||||
Bridge to mastodon
|
||||
|
||||
![Post on mastodon, same text but with link to the git commit](img/git-to-masto_1.png)
|
||||
|
||||
## From masto to git
|
||||
|
||||
Post on mastodon...
|
||||
|
||||
![Post on mastodon: This is the future of decentralized communication](img/masto-to-git_0.png)
|
||||
|
||||
Bridge to git-social
|
||||
|
||||
![Post on git social: same post, but with link to tweet](img/masto-to-git_1.png)
|
||||
|
||||
# Features
|
||||
|
||||
Everything in this package is a bug and not a feature.
|
||||
|
||||
# Setup
|
||||
|
||||
## Installation
|
||||
|
||||
I'm not going to uh, make this good or put it on pypi or anything.
|
||||
|
||||
So you should clone this and install it with poetry, (otherwise
|
||||
you have to modify the below `post-commit` action to activate the
|
||||
venv where it is installed correctly)
|
||||
|
||||
```shell
|
||||
git clone https://git.jon-e.net/jonny/masto-gitsocial-bridge
|
||||
cd masto-gitsocial-bridge
|
||||
poetry install
|
||||
```
|
||||
|
||||
## Make masto token
|
||||
|
||||
~ check with your instance's policies before doing something bad like this ~
|
||||
|
||||
From your masto instance's homepage...
|
||||
|
||||
- Preferences (in hamburger menu top right if page is narrow)
|
||||
- Development tab
|
||||
- New Application
|
||||
|
||||
The bot needs permissions (I'm honestly not sure you need to give all of `read`, but
|
||||
`Mastodon.py`'s `me()` method seems to need a lot of them. idk.)
|
||||
- `read`
|
||||
- `write:lists` - the bot only streams your posts by making a list with just you on it
|
||||
- `write:statuses` - to xpost, dummy!
|
||||
|
||||
Then copy the resulting access token for use in configuration...
|
||||
|
||||
## Config
|
||||
|
||||
See `masto_git_bridge.config.Config` for the required configuration values.
|
||||
|
||||
The config object uses Pydantic to load either from an `.env` file or
|
||||
from environment variables, for example, make an `.env` file in the cloned
|
||||
repository directory like
|
||||
|
||||
```
|
||||
MASTOGIT_MASTO_URL="https://social.coop"
|
||||
MASTOGIT_MASTO_TOKEN="<mastodon bot access token>"
|
||||
MASTOGIT_GIT_REPO="/path/to/your/git-social/tweets"
|
||||
MASTOGIT_GIT_REMOTE_URL="https://git.jon-e.net/jonny/tweets"
|
||||
MASTOGIT_LOGDIR="/wherever/you/want/to/put/logs"
|
||||
```
|
||||
|
||||
This is assuming you don't need any sort of authentication/local password
|
||||
on your local git repository in order to commit or push to it.
|
||||
|
||||
## `post-commit` action
|
||||
|
||||
In your git-social repository, make a `post-commit` action (`.git/hooks/post-commit`)
|
||||
that looks something like this (see the [sample](post-commit.sample))
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Assuming we have installed the package using poetry from a git repository
|
||||
# lmao I did not say we handled virtual environments well in this package
|
||||
|
||||
cd <path/to/masto-gitsocial-bridge>
|
||||
poetry run post_last_commit
|
||||
|
||||
# otherwise activate whatever venv you have installed the package in and
|
||||
# call masto_git_bridge.main:post_last_commit, which is
|
||||
# installed as an entrypoint script by poetry
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
The post-commit action should run anytime you commit a post to git-social, but to post
|
||||
from mastodon you'll have to run the bot, which listens for your posts and reposts them to
|
||||
git-social (only if they are "public" or "unlisted").
|
||||
|
||||
```bash
|
||||
poetry run masto_gitbot
|
||||
# or
|
||||
# >>> poetry shell
|
||||
# >>> masto_gitbot
|
||||
# or however else you run python entrypoint scripts
|
||||
# hell you could do python -m masto_git_bridge.main:masto_gitbot
|
||||
# i think?
|
||||
```
|
||||
|
||||
# Warnings & Gotchas
|
||||
|
||||
If you ever enable this, you should promptly disable it
|
BIN
img/git-to-masto_0.png
Normal file
BIN
img/git-to-masto_0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
img/git-to-masto_1.png
Normal file
BIN
img/git-to-masto_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
BIN
img/masto-to-git_0.png
Normal file
BIN
img/masto-to-git_0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
BIN
img/masto-to-git_1.png
Normal file
BIN
img/masto-to-git_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 135 KiB |
|
@ -1,40 +1,106 @@
|
|||
import pdb
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from masto_git_bridge.config import Config
|
||||
from masto_git_bridge.models import Account
|
||||
from masto_git_bridge.models import Account, List
|
||||
from masto_git_bridge.post import Post, Status
|
||||
from masto_git_bridge.logger import init_logger
|
||||
from masto_git_bridge.repo import Repo
|
||||
|
||||
from mastodon import Mastodon, StreamListener
|
||||
|
||||
class Listener(StreamListener):
|
||||
def __init__(self, client: Mastodon, config:Optional[Config]=None):
|
||||
super(Listener, self).__init__()
|
||||
self.client = client
|
||||
|
||||
if config is None:
|
||||
config = Config()
|
||||
|
||||
self.config = config
|
||||
|
||||
self.logger = init_logger('mastogit_bot-stream', basedir=self.config.LOGDIR)
|
||||
|
||||
self.repo = Repo(path=config.GIT_REPO)
|
||||
|
||||
|
||||
|
||||
def on_update(self, status:dict):
|
||||
status = Status(**status)
|
||||
if status.visibility in ('private', 'direct'):
|
||||
# not xposting dms
|
||||
self.logger.info('Not xposting private messages')
|
||||
return
|
||||
|
||||
post = Post.from_status(status)
|
||||
if post.text.startswith('xpost'):
|
||||
self.logger.info('Not xposting an xpost')
|
||||
return
|
||||
|
||||
success = self.repo.post(post.format_commit())
|
||||
if success:
|
||||
self.logger.info('Posted to git!')
|
||||
else:
|
||||
self.logger.exception('Failed to post to git!')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Bot:
|
||||
def __init__(self, config:Optional[Config]=None):
|
||||
def __init__(self, config:Optional[Config]=None, post_length=500):
|
||||
self._me = None # type: Optional[Account]
|
||||
self._me_list = None # type: Optional[List]
|
||||
|
||||
if config is None:
|
||||
config = Config()
|
||||
|
||||
self.config = config
|
||||
self.config.LOGDIR.mkdir(exist_ok=True)
|
||||
self.post_length = post_length
|
||||
self.logger = init_logger('mastogit_bot', basedir=self.config.LOGDIR)
|
||||
|
||||
self.client = Mastodon(
|
||||
access_token=self.config.MASTO_TOKEN,
|
||||
api_base_url=self.config.MASTO_URL
|
||||
)
|
||||
|
||||
def init_stream(self, run_async:bool=True):
|
||||
# Listen to a stream consisting of just us.
|
||||
listener = Listener(client=self.client, config=self.config)
|
||||
self.logger.info('Initializing streaming')
|
||||
self.client.stream_list(
|
||||
self.me_list.id,
|
||||
listener = listener,
|
||||
run_async=run_async
|
||||
)
|
||||
|
||||
def post(self, post:str):
|
||||
# TODO: Split long posts
|
||||
if len(post)>self.post_length:
|
||||
raise NotImplementedError(f"Cant split long posts yet, got post of length {len(post)} when max length is {self.post_length}")
|
||||
|
||||
self.client.status_post(post)
|
||||
self.logger.info(f"Posted:\n{post}")
|
||||
|
||||
@property
|
||||
def me(self) -> Account:
|
||||
if self._me is None:
|
||||
self._me = Account(**self.client.me())
|
||||
return self._me
|
||||
|
||||
def _make_me_list(self) -> List:
|
||||
me_list = List(**self.client.list_create('me'))
|
||||
self.client.list_accounts_add(me_list.id, [self.me.id])
|
||||
self.logger.info('Created list with just me in it!')
|
||||
return me_list
|
||||
|
||||
@property
|
||||
def me_list(self) -> List:
|
||||
if self._me_list is None:
|
||||
lists = self.client.lists()
|
||||
me_list = [l for l in lists if l.get('title', '') == 'me']
|
||||
if len(me_list)>0:
|
||||
self._me_list = List(**me_list[0])
|
||||
else:
|
||||
self._me_list = self._make_me_list()
|
||||
return self._me_list
|
||||
|
|
56
masto_git_bridge/logger.py
Normal file
56
masto_git_bridge/logger.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
import logging
|
||||
from rich.logging import RichHandler
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import typing
|
||||
from typing import Optional, Union, Tuple, List, Dict, Literal
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
def init_logger(
|
||||
name:Optional[str]=None,
|
||||
basedir:Optional[Path]=None,
|
||||
loglevel:str='DEBUG',
|
||||
loglevel_disk:Optional[str]='DEBUG'
|
||||
):
|
||||
if name is None:
|
||||
name = 'wiki_postbot'
|
||||
else:
|
||||
if not name.startswith('wiki_postbot'):
|
||||
name = '.'.join(['wiki_postbot', name])
|
||||
|
||||
if loglevel_disk is None:
|
||||
loglevel_disk = loglevel
|
||||
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(loglevel)
|
||||
|
||||
|
||||
if basedir is not None:
|
||||
logger.addHandler(_file_handler(basedir, name, loglevel_disk))
|
||||
|
||||
logger.addHandler(_rich_handler())
|
||||
return logger
|
||||
|
||||
|
||||
def _file_handler(basedir:Path, name:str, loglevel:str="DEBUG") -> RotatingFileHandler:
|
||||
filename = Path(basedir) / '.'.join([name, 'log'])
|
||||
basedir.mkdir(parents=True, exist_ok=True)
|
||||
file_handler = RotatingFileHandler(
|
||||
str(filename),
|
||||
mode='a',
|
||||
maxBytes=2 ** 24,
|
||||
backupCount=5
|
||||
)
|
||||
file_formatter = logging.Formatter("[%(asctime)s] %(levelname)s [%(name)s]: %(message)s")
|
||||
file_handler.setLevel(loglevel)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
return file_handler
|
||||
|
||||
def _rich_handler() -> RichHandler:
|
||||
rich_handler = RichHandler(rich_tracebacks=True, markup=True)
|
||||
rich_formatter = logging.Formatter(
|
||||
"[bold green]\[%(name)s][/bold green] %(message)s",
|
||||
datefmt='[%y-%m-%dT%H:%M:%S]'
|
||||
)
|
||||
rich_handler.setFormatter(rich_formatter)
|
||||
return rich_handler
|
|
@ -1,10 +1,42 @@
|
|||
from typing import Optional
|
||||
from masto_git_bridge.config import Config
|
||||
from masto_git_bridge.repo import Repo
|
||||
from masto_git_bridge.bot import Bot
|
||||
from masto_git_bridge.post import Post
|
||||
from masto_git_bridge.logger import init_logger
|
||||
from time import sleep
|
||||
|
||||
def post_last_commit(config:Optional[Config]=None):
|
||||
"""
|
||||
Should be triggered as a commit hook because it doesn't validate
|
||||
the last commit hasn't already been posted.
|
||||
"""
|
||||
if config is None:
|
||||
config = Config()
|
||||
logger = init_logger('post-git', basedir=config.LOGDIR)
|
||||
|
||||
repo = Repo(config.GIT_REPO)
|
||||
last_commit = repo.last_commit
|
||||
post = Post.from_commit(last_commit)
|
||||
|
||||
if post.text.startswith('xpost'):
|
||||
logger.info('Not xposting an xpost')
|
||||
return
|
||||
|
||||
|
||||
bot = Bot(config=config)
|
||||
bot.post(post.format_masto())
|
||||
|
||||
def masto_gitbot(config:Optional[Config]=None):
|
||||
if config is None:
|
||||
config = Config()
|
||||
|
||||
bot = Bot(config=config)
|
||||
try:
|
||||
bot.init_stream()
|
||||
while True:
|
||||
sleep(60*60)
|
||||
bot.logger.info('taking a breath')
|
||||
except KeyboardInterrupt:
|
||||
bot.logger.info('quitting!')
|
||||
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
from pydantic import BaseModel
|
||||
|
||||
class List(BaseModel):
|
||||
"""A mastodon list!"""
|
||||
id: str
|
||||
title: str
|
||||
|
||||
class Config:
|
||||
extra = 'ignore'
|
||||
|
||||
class Account(BaseModel):
|
||||
"""Not transcribing full model now, just using to check"""
|
||||
acct: str
|
||||
|
|
|
@ -1,8 +1,68 @@
|
|||
from typing import Optional
|
||||
from typing import Optional, Literal
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
import re
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from masto_git_bridge.repo import Commit
|
||||
from masto_git_bridge.models import Account
|
||||
|
||||
class Status(BaseModel):
|
||||
"""
|
||||
Model of a toot on mastodon
|
||||
|
||||
See: https://mastodonpy.readthedocs.io/en/stable/#toot-dicts
|
||||
"""
|
||||
id: int
|
||||
url: str
|
||||
account: Account
|
||||
content: str
|
||||
visibility: Literal['public', 'unlisted', 'private', 'direct']
|
||||
in_reply_to_id: Optional[int] = None
|
||||
in_reply_to_account_id: Optional[int] = None
|
||||
|
||||
class Config:
|
||||
extra='ignore'
|
||||
|
||||
class Post(BaseModel):
|
||||
timestamp: datetime
|
||||
hash: Optional[str]
|
||||
#timestamp: Optional[datetime] = None
|
||||
text:str
|
||||
status:Optional[Status] = None
|
||||
commit:Optional[Commit] = None
|
||||
|
||||
@classmethod
|
||||
def from_commit(cls, commit:Commit) -> 'Post':
|
||||
text = '\n'.join([commit.subject, commit.body])
|
||||
return Post(text=text, commit=commit)
|
||||
|
||||
@classmethod
|
||||
def from_status(cls, status:Status) -> 'Post':
|
||||
# split paragraphs using bs4
|
||||
soup = BeautifulSoup(status.content, 'lxml')
|
||||
# replace with double line breaks
|
||||
pars = [p.text for p in soup.find_all('p')]
|
||||
text = '\n\n'.join(pars)
|
||||
return Post(text=text, status=status)
|
||||
|
||||
def format_masto(self) -> str:
|
||||
"""
|
||||
Format a post to go from git -> masto.
|
||||
|
||||
Needs to have a :attr:`.commit` attribute!
|
||||
|
||||
Does not split the body text into multiple toots.
|
||||
That should be handled in the posting action
|
||||
|
||||
Example:
|
||||
|
||||
git-social: https://{repo_url}/commits/{hash}
|
||||
{subject line}
|
||||
{body}
|
||||
"""
|
||||
return f"xpost from git-social: {self.commit.url}\n---\n{self.text}"""
|
||||
|
||||
def format_commit(self) -> str:
|
||||
"""
|
||||
Add a link back to original masto post split by double lines
|
||||
"""
|
||||
return f"xpost from mastodon: {self.status.url}\n\n{self.text}"
|
|
@ -1,9 +1,10 @@
|
|||
import pdb
|
||||
from pathlib import Path
|
||||
from subprocess import run
|
||||
import json
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from pydantic import BaseModel, EmailStr, AnyHttpUrl
|
||||
|
||||
log_format = '{%n "commit": "%H",%n "abbreviated_commit": "%h",%n "tree": "%T",%n "abbreviated_tree": "%t",%n "parent": "%P",%n "abbreviated_parent": "%p",%n "refs": "%D",%n "encoding": "%e",%n "subject": "%s",%n "sanitized_subject_line": "%f",%n "body": "%b",%n "commit_notes": "%N",%n "verification_flag": "%G?",%n "signer": "%GS",%n "signer_key": "%GK",%n "author": {%n "name": "%aN",%n "email": "%aE",%n "date": "%aD"%n },%n "commiter": {%n "name": "%cN",%n "email": "%cE",%n "date": "%cD"%n }%n}'
|
||||
"""Thanks https://gist.github.com/varemenos/e95c2e098e657c7688fd"""
|
||||
|
@ -20,9 +21,11 @@ class Commit(BaseModel):
|
|||
body:str
|
||||
author: Author
|
||||
commiter: Author
|
||||
origin_url: AnyHttpUrl
|
||||
|
||||
def make_url(self, remote_url:str):
|
||||
return urljoin(remote_url, f'commit/{self.abbreviated_commit}')
|
||||
@property
|
||||
def url(self) -> str:
|
||||
return urljoin(self.origin_url + '/', f'commit/{self.abbreviated_commit}')
|
||||
|
||||
class Config:
|
||||
extra='ignore'
|
||||
|
@ -31,6 +34,33 @@ class Repo:
|
|||
def __init__(self, path:Path):
|
||||
self.path = Path(path)
|
||||
|
||||
def post(self, post:str) -> bool:
|
||||
# make paragraphs by splitting \n\n
|
||||
paras = []
|
||||
for para in post.split('\n\n'):
|
||||
paras.extend(('-m', para))
|
||||
|
||||
path = self.path
|
||||
command = [
|
||||
'git',
|
||||
'-C', str(path),
|
||||
'commit',
|
||||
*paras,
|
||||
'--allow-empty'
|
||||
]
|
||||
output = run(command, capture_output=True)
|
||||
if output.returncode != 0:
|
||||
return False
|
||||
|
||||
output = run([
|
||||
'git',
|
||||
'-C', str(path),
|
||||
'push'
|
||||
], capture_output=True)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def last_commit(self) -> Commit:
|
||||
path = self.path
|
||||
|
@ -44,4 +74,17 @@ class Repo:
|
|||
capture_output=True
|
||||
)
|
||||
parse = json.loads(output.stdout, strict=False)
|
||||
return Commit(**parse)
|
||||
return Commit(origin_url=self.origin_url, **parse)
|
||||
|
||||
@property
|
||||
def origin_url(self) -> str:
|
||||
path = self.path
|
||||
output = run([
|
||||
"git",
|
||||
'-C', str(path),
|
||||
'remote',
|
||||
'get-url',
|
||||
'origin'
|
||||
], capture_output=True)
|
||||
url = output.stdout.decode('utf-8').strip().rstrip('.git')
|
||||
return url
|
||||
|
|
168
poetry.lock
generated
168
poetry.lock
generated
|
@ -1,3 +1,18 @@
|
|||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.11.1"
|
||||
description = "Screen-scraping library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.0"
|
||||
|
||||
[package.dependencies]
|
||||
soupsieve = ">1.2"
|
||||
|
||||
[package.extras]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "blurhash"
|
||||
version = "1.1.4"
|
||||
|
@ -28,6 +43,17 @@ python-versions = ">=3.6.0"
|
|||
[package.extras]
|
||||
unicode-backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "commonmark"
|
||||
version = "0.9.1"
|
||||
description = "Python parser for the CommonMark Markdown spec"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.1.1"
|
||||
|
@ -72,6 +98,20 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "lxml"
|
||||
version = "4.9.1"
|
||||
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
|
||||
|
||||
[package.extras]
|
||||
cssselect = ["cssselect (>=0.7)"]
|
||||
html5 = ["html5lib"]
|
||||
htmlsoup = ["BeautifulSoup4"]
|
||||
source = ["Cython (>=0.29.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "mastodon-py"
|
||||
version = "1.5.2"
|
||||
|
@ -111,6 +151,17 @@ typing-extensions = ">=4.1.0"
|
|||
dotenv = ["python-dotenv (>=0.10.4)"]
|
||||
email = ["email-validator (>=1.0.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.13.0"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
plugins = ["importlib-metadata"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.8.2"
|
||||
|
@ -167,6 +218,21 @@ urllib3 = ">=1.21.1,<1.27"
|
|||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "12.6.0"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.3,<4.0.0"
|
||||
|
||||
[package.dependencies]
|
||||
commonmark = ">=0.9.0,<0.10.0"
|
||||
pygments = ">=2.6.0,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
|
@ -175,6 +241,14 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.3.2.post1"
|
||||
description = "A modern CSS selector implementation for Beautiful Soup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.4.0"
|
||||
|
@ -199,9 +273,13 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "61c87bda2ae45e82c6ed4dca57ad766a01078f32838844c10832c9e5f5790ab3"
|
||||
content-hash = "c85c922dc9bcac039fd7b4b5f12d310934ae4faa2bd23c426c2a3beb62e86721"
|
||||
|
||||
[metadata.files]
|
||||
beautifulsoup4 = [
|
||||
{file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"},
|
||||
{file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"},
|
||||
]
|
||||
blurhash = [
|
||||
{file = "blurhash-1.1.4-py2.py3-none-any.whl", hash = "sha256:7611c1bc41383d2349b6129208587b5d61e8792ce953893cb49c38beeb400d1d"},
|
||||
{file = "blurhash-1.1.4.tar.gz", hash = "sha256:da56b163e5a816e4ad07172f5639287698e09d7f3dc38d18d9726d9c1dbc4cee"},
|
||||
|
@ -214,6 +292,10 @@ charset-normalizer = [
|
|||
{file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
|
||||
{file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
|
||||
]
|
||||
commonmark = [
|
||||
{file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"},
|
||||
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
|
||||
]
|
||||
decorator = [
|
||||
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
|
||||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||
|
@ -230,6 +312,78 @@ idna = [
|
|||
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
|
||||
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
|
||||
]
|
||||
lxml = [
|
||||
{file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"},
|
||||
{file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"},
|
||||
{file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc"},
|
||||
{file = "lxml-4.9.1-cp27-cp27m-win32.whl", hash = "sha256:86e92728ef3fc842c50a5cb1d5ba2bc66db7da08a7af53fb3da79e202d1b2cd3"},
|
||||
{file = "lxml-4.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4cfbe42c686f33944e12f45a27d25a492cc0e43e1dc1da5d6a87cbcaf2e95627"},
|
||||
{file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dad7b164905d3e534883281c050180afcf1e230c3d4a54e8038aa5cfcf312b84"},
|
||||
{file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a614e4afed58c14254e67862456d212c4dcceebab2eaa44d627c2ca04bf86837"},
|
||||
{file = "lxml-4.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f9ced82717c7ec65a67667bb05865ffe38af0e835cdd78728f1209c8fffe0cad"},
|
||||
{file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:d9fc0bf3ff86c17348dfc5d322f627d78273eba545db865c3cd14b3f19e57fa5"},
|
||||
{file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e5f66bdf0976ec667fc4594d2812a00b07ed14d1b44259d19a41ae3fff99f2b8"},
|
||||
{file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fe17d10b97fdf58155f858606bddb4e037b805a60ae023c009f760d8361a4eb8"},
|
||||
{file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8caf4d16b31961e964c62194ea3e26a0e9561cdf72eecb1781458b67ec83423d"},
|
||||
{file = "lxml-4.9.1-cp310-cp310-win32.whl", hash = "sha256:4780677767dd52b99f0af1f123bc2c22873d30b474aa0e2fc3fe5e02217687c7"},
|
||||
{file = "lxml-4.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a188cd292c4d2fcd78d04f863b789ef43aa129b233d7c9004de08693728b"},
|
||||
{file = "lxml-4.9.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:be9eb06489bc975c38706902cbc6888f39e946b81383abc2838d186f0e8b6a9d"},
|
||||
{file = "lxml-4.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f1be258c4d3dc609e654a1dc59d37b17d7fef05df912c01fc2e15eb43a9735f3"},
|
||||
{file = "lxml-4.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:927a9dd016d6033bc12e0bf5dee1dde140235fc8d0d51099353c76081c03dc29"},
|
||||
{file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9232b09f5efee6a495a99ae6824881940d6447debe272ea400c02e3b68aad85d"},
|
||||
{file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:04da965dfebb5dac2619cb90fcf93efdb35b3c6994fea58a157a834f2f94b318"},
|
||||
{file = "lxml-4.9.1-cp35-cp35m-win32.whl", hash = "sha256:4d5bae0a37af799207140652a700f21a85946f107a199bcb06720b13a4f1f0b7"},
|
||||
{file = "lxml-4.9.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4878e667ebabe9b65e785ac8da4d48886fe81193a84bbe49f12acff8f7a383a4"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:1355755b62c28950f9ce123c7a41460ed9743c699905cbe664a5bcc5c9c7c7fb"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:bcaa1c495ce623966d9fc8a187da80082334236a2a1c7e141763ffaf7a405067"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eafc048ea3f1b3c136c71a86db393be36b5b3d9c87b1c25204e7d397cee9536"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:13c90064b224e10c14dcdf8086688d3f0e612db53766e7478d7754703295c7c8"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206a51077773c6c5d2ce1991327cda719063a47adc02bd703c56a662cdb6c58b"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e8f0c9d65da595cfe91713bc1222af9ecabd37971762cb830dea2fc3b3bb2acf"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0a4d179c9a941eb80c3a63cdb495e539e064f8054230844dcf2fcb812b71d3"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:830c88747dce8a3e7525defa68afd742b4580df6aa2fdd6f0855481e3994d391"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-win32.whl", hash = "sha256:1e1cf47774373777936c5aabad489fef7b1c087dcd1f426b621fda9dcc12994e"},
|
||||
{file = "lxml-4.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:5974895115737a74a00b321e339b9c3f45c20275d226398ae79ac008d908bff7"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1423631e3d51008871299525b541413c9b6c6423593e89f9c4cfbe8460afc0a2"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:2aaf6a0a6465d39b5ca69688fce82d20088c1838534982996ec46633dc7ad6cc"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:9f36de4cd0c262dd9927886cc2305aa3f2210db437aa4fed3fb4940b8bf4592c"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae06c1e4bc60ee076292e582a7512f304abdf6c70db59b56745cca1684f875a4"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:57e4d637258703d14171b54203fd6822fda218c6c2658a7d30816b10995f29f3"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d279033bf614953c3fc4a0aa9ac33a21e8044ca72d4fa8b9273fe75359d5cca"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a60f90bba4c37962cbf210f0188ecca87daafdf60271f4c6948606e4dabf8785"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2264f341dd81e41f3fffecec6e446aa2121e0b8d026fb5130e02de1402785"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-win32.whl", hash = "sha256:27e590352c76156f50f538dbcebd1925317a0f70540f7dc8c97d2931c595783a"},
|
||||
{file = "lxml-4.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:eea5d6443b093e1545ad0210e6cf27f920482bfcf5c77cdc8596aec73523bb7e"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f05251bbc2145349b8d0b77c0d4e5f3b228418807b1ee27cefb11f69ed3d233b"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:487c8e61d7acc50b8be82bda8c8d21d20e133c3cbf41bd8ad7eb1aaeb3f07c97"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d1a92d8e90b286d491e5626af53afef2ba04da33e82e30744795c71880eaa21"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b570da8cd0012f4af9fa76a5635cd31f707473e65a5a335b186069d5c7121ff2"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ef87fca280fb15342726bd5f980f6faf8b84a5287fcc2d4962ea8af88b35130"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:93e414e3206779ef41e5ff2448067213febf260ba747fc65389a3ddaa3fb8715"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6653071f4f9bac46fbc30f3c7838b0e9063ee335908c5d61fb7a4a86c8fd2036"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:32a73c53783becdb7eaf75a2a1525ea8e49379fb7248c3eeefb9412123536387"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-win32.whl", hash = "sha256:1a7c59c6ffd6ef5db362b798f350e24ab2cfa5700d53ac6681918f314a4d3b94"},
|
||||
{file = "lxml-4.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:1436cf0063bba7888e43f1ba8d58824f085410ea2025befe81150aceb123e345"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:4beea0f31491bc086991b97517b9683e5cfb369205dac0148ef685ac12a20a67"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:41fb58868b816c202e8881fd0f179a4644ce6e7cbbb248ef0283a34b73ec73bb"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bd34f6d1810d9354dc7e35158aa6cc33456be7706df4420819af6ed966e85448"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:edffbe3c510d8f4bf8640e02ca019e48a9b72357318383ca60e3330c23aaffc7"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d949f53ad4fc7cf02c44d6678e7ff05ec5f5552b235b9e136bd52e9bf730b91"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:079b68f197c796e42aa80b1f739f058dcee796dc725cc9a1be0cdb08fc45b000"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9c3a88d20e4fe4a2a4a84bf439a5ac9c9aba400b85244c63a1ab7088f85d9d25"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4e285b5f2bf321fc0857b491b5028c5f276ec0c873b985d58d7748ece1d770dd"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-win32.whl", hash = "sha256:ef72013e20dd5ba86a8ae1aed7f56f31d3374189aa8b433e7b12ad182c0d2dfb"},
|
||||
{file = "lxml-4.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:10d2017f9150248563bb579cd0d07c61c58da85c922b780060dcc9a3aa9f432d"},
|
||||
{file = "lxml-4.9.1-pp37-pypy37_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538747a9d7827ce3e16a8fdd201a99e661c7dee3c96c885d8ecba3c35d1032c"},
|
||||
{file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0645e934e940107e2fdbe7c5b6fb8ec6232444260752598bc4d09511bd056c0b"},
|
||||
{file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6daa662aba22ef3258934105be2dd9afa5bb45748f4f702a3b39a5bf53a1f4dc"},
|
||||
{file = "lxml-4.9.1-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:603a464c2e67d8a546ddaa206d98e3246e5db05594b97db844c2f0a1af37cf5b"},
|
||||
{file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c4b2e0559b68455c085fb0f6178e9752c4be3bba104d6e881eb5573b399d1eb2"},
|
||||
{file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0f3f0059891d3254c7b5fb935330d6db38d6519ecd238ca4fce93c234b4a0f73"},
|
||||
{file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c852b1530083a620cb0de5f3cd6826f19862bafeaf77586f1aef326e49d95f0c"},
|
||||
{file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"},
|
||||
{file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"},
|
||||
]
|
||||
mastodon-py = [
|
||||
{file = "Mastodon.py-1.5.2-py2.py3-none-any.whl", hash = "sha256:49afbf9f4347f355bee5638b71a5231b0e1164a58d253ccd9b4345d999c43369"},
|
||||
{file = "Mastodon.py-1.5.2.tar.gz", hash = "sha256:c98fd97b7450cd02262669b80be20f53657b5540c4888a47231df11856910918"},
|
||||
|
@ -272,6 +426,10 @@ pydantic = [
|
|||
{file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
|
||||
{file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
|
||||
]
|
||||
pygments = [
|
||||
{file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"},
|
||||
{file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"},
|
||||
]
|
||||
python-dateutil = [
|
||||
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
||||
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
||||
|
@ -292,10 +450,18 @@ requests = [
|
|||
{file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
|
||||
{file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
|
||||
]
|
||||
rich = [
|
||||
{file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"},
|
||||
{file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"},
|
||||
]
|
||||
six = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
soupsieve = [
|
||||
{file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
|
||||
{file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
|
||||
{file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
|
||||
|
|
10
post-commit.sample
Normal file
10
post-commit.sample
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
# Assuming we have installed the package using poetry from a git repository
|
||||
# lmao I did not say we handled virtual environments well in this package
|
||||
|
||||
cd <path/to/masto-gitsocial-bridge>
|
||||
poetry run post_last_commit
|
||||
|
||||
# otherwise activate whatever venv you have installed the package in and
|
||||
# call masto_git_bridge.main:post_last_commit, which is
|
||||
# installed as an entrypoint script by poetry
|
|
@ -11,7 +11,13 @@ packages = [{include = "masto_git_bridge"}]
|
|||
python = "^3.9"
|
||||
"Mastodon.py" = "^1.5.2"
|
||||
pydantic = {extras = ["dotenv", "email"], version = "^1.10.2"}
|
||||
rich = "^12.6.0"
|
||||
beautifulsoup4 = "^4.11.1"
|
||||
lxml = "^4.9.1"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
post_last_commit = 'masto_git_bridge.main:post_last_commit'
|
||||
masto_gitbot = 'masto_git_bridge.main:masto_gitbot'
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
|
|
Loading…
Reference in a new issue