start of project

This commit is contained in:
Morgan McMillian 2022-04-30 07:05:18 -07:00
commit 284f6b2bd0
5 changed files with 226 additions and 0 deletions

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2022 Morgan McMillian
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

10
README.md Normal file
View file

@ -0,0 +1,10 @@
# feedbot
My quick and dirty cross posting script to take posts from Mastodon and cross post to pnut.io.
## Contributing
Report bugs, send patches to [~thrrgilag/public-inbox@lists.sr.ht](https://lists.sr.ht/~thrrgilag/public-inbox).
Discussion also in [thrrgilag's dev chat](https://thrrgilag.net/chat/dev).

142
feedbot.py Normal file
View file

@ -0,0 +1,142 @@
import feedparser
import requests
import logging
import pnutpy
import click
import re
import os
import models as zdb
from PIL import ImageFile
SNAP_USER_DATA = os.environ.get('SNAP_USER_DATA')
@click.group(invoke_without_command=True)
@click.pass_context
@click.option('--db')
@click.option('--prime', is_flag=True)
@click.option('--debug', is_flag=True)
@click.version_option(version='0.1.0')
def main(ctx, db, prime, debug):
if debug:
logging.basicConfig(level=logging.DEBUG)
if SNAP_USER_DATA is None and db is not None:
db_file = db
else:
db_file = SNAP_USER_DATA + '/feeds.db'
zdb.db.init(db_file)
zdb.create_tables()
if ctx.invoked_subcommand is None:
for feed in zdb.Feeds.select():
fetch(feed.url, feed.pnut_uid, feed.id, prime)
@main.command()
@click.argument('username')
@click.argument('token')
def adduser(username, token):
'''Add a pnut user'''
pnutpy.api.add_authorization_token(token)
pass
try:
pnut_user, meta = pnutpy.api.get_user('me')
user = zdb.User(pnut_uid=pnut_user.id, pnut_token=token, pnut_enabled=True)
user.save()
except pnutpy.errors.PnutAuthAPIException:
logging.error(f"pnut user token not valid")
@main.command()
@click.argument('uid')
@click.argument('url')
def add(uid, url):
'''Add a feed'''
feed = zdb.Feeds(pnut_uid=uid, url=url)
feed.save()
def fetch(url, pnut_uid, fid, prime):
feed = feedparser.parse(url)
try:
user = zdb.User.get(pnut_uid=pnut_uid)
source = {
'link': feed.feed.link,
'username': feed.feed.title,
'avatar': feed.feed.image.href
}
for post in reversed(feed.entries):
link = post.link
try:
entry = zdb.Entries.get(feedid=fid, link=link)
logging.debug(f"skipping {link}...")
except zdb.Entries.DoesNotExist:
entry = zdb.Entries(feedid=fid, link=link)
entry.save()
if prime:
logging.debug(f"saving {link}...")
else:
logging.debug(f"posting {link}...")
pnutpost(post, source, user.pnut_token)
except zdb.User.DoesNotExist:
logging.error(f"user {pnut_uid} not found")
except Exception:
logging.exception("bad stuff")
def pnutpost(entry, source, token):
pnutpy.api.add_authorization_token(token)
crosspost = {
'type': "io.pnut.core.crosspost",
'value': {
'canonical_url': entry.link,
#'source': {'url': source['link']},
'user': {
'username': source['username'],
'avatar_image': source['avatar']
}
}
}
raw = [crosspost]
for link in entry.links:
if link.rel == "enclosure":
if "image" in link.type:
raw.append(embed_image(link))
try:
rx = re.compile('<.*?>')
text = re.sub(rx, '', entry.summary)
p, meta = pnutpy.api.create_post(data={'text': text, 'raw': raw})
except Exception:
logging.exception("bad stuff")
def embed_image(link):
resume_header = {'Range': 'bytes=0-2000000'}
r = requests.get(link.href, stream=True, headers=resume_header)
p = ImageFile.Parser()
p.feed(r.content)
if p.image:
width, height = p.image.size
embed = {
'version': "1.0",
'type': "photo",
'width': width,
'height': height,
'url': link.href
}
return {'type': "io.pnut.core.oembed", 'value': embed}
else:
return {}

29
models.py Normal file
View file

@ -0,0 +1,29 @@
from peewee import *
db = SqliteDatabase(None)
class BaseModel(Model):
class Meta:
database = db
class Feeds(BaseModel):
url = CharField()
pnut_uid = CharField()
class Entries(BaseModel):
feedid = IntegerField()
link = CharField()
class User(BaseModel):
pnut_uid = CharField(unique=True)
pnut_token = CharField(null=True)
pnut_enabled = BooleanField(default=False)
class System(BaseModel):
key = CharField(unique=True)
value = CharField()
def create_tables():
with db:
db.create_tables([Feeds, Entries, User, System])

23
setup.py Normal file
View file

@ -0,0 +1,23 @@
from setuptools import setup
setup(
name='feedbot',
version='0.1.0',
py_modules=[
'feedbot',
],
install_requires=[
'requests',
'pnutpy',
'click',
'feedparser',
'peewee',
'pillow',
],
entry_points={
'console_scripts': [
'feedbot = feedbot:main',
]
},
)