Giter Site home page Giter Site logo

cohost.py's Introduction

Cohost.py

Edit of Eggbug into the python logo

A python library for Cohost!

Hyperstart

pip install cohost

Quickstart

pip install cohost

from cohost.models.user import User
from cohost.models.block import AttachmentBlock, MarkdownBlock

cookie = 'yourCookie'
user = User.loginWithCookie(cookie)
# Alternatively: use a username/password combo
# import os
# user = User.login(os.getenv("COHOST_USER"), os.getenv("COHOST_PASS"))

for project in user.editedProjects:
    print(project) # Print all pages you have edit permissions for
project = user.getProject('vallerie') # will retrieve the page I have edit writes for with handle @vallerie

blocks = [
    AttachmentBlock('pybug.png'), # References image file pybug.png
    MarkdownBlock('**Hello from Python!**') # Example of markdown / text block
]
newPost = project.post('Title of python post!', blocks, tags=['cohost.py', 'python'])
print('Check out your post at {}'.format(newPost.url))

Getting started

  1. Have an activated Cohost.org account. This entire library will probably explode if you use it with an unactivated account, and it defo isn't some bypass.
  2. Install library with pip install cohost
  3. Log in with a cookie or a username/password
  4. Import data models you require, and go from there!

Terminology

Some things are different on the API, and on the UI, and for the most part this library will match the API's terminology. Some key concepts:

  • A user is minimal - it will contain authentication, email, and some other key details but not much else
  • All the pages you can edit are referred to as "projects"
  • Each post is made up content "Blocks"

Retrieving your cookie

One way to login is to use an authentication token for Cohost. This can be retrieved by:

  1. Open Developer Tools in your browser
  2. Go to "Storage"
  3. Find the "Cookies" entry
  4. Copy the data for "connect.sid".
  5. Use this in the library

What's working? (allegedly)

  • Logging in as a user (using either a cookie or a username/password)
  • Retrieving projects of a user (and of other people when you got a post to go with it)
  • Retrieving posts of a page
  • Posting to a page
  • Attaching images to a post (with alt-text support!)
  • Editing posts
  • Retrieving notifications with pagination

What's not done but needs to be done?

  • Retrieving single posts - currently have to read entire projects
  • Retrieving a project's drafts
  • Retrieving a projects of others without a post inbetween
  • Sharing posts (with comment)
  • Editing profiles
  • Deleting posts
  • Likely a whole bunch of other things I haven't thought about
  • Better docs

What's not implemented intentionally?

Some features I intend not to add. These features aren't impossible to build, but, they could be detrimental to the Cohost experience for other users, send mass notifications without an account being activated, or pose security issues. This is designed to deter low effort malicious bots, to reduce the workload on Cohost's staff.

These include:

  • Editing a user's password
  • Accepting follow requests
  • Sharing a post without a comment
  • Creating new projects

If you implement these features, please keep them private for your projects. If you think one of these should be implemented, please file a GitHub issue with your case as to why.

Support Cohost

Buy Cohost PLUS

Follow me on Cohost!

hello is me

Thanks

  • cohost.js - provided a good point to start looking at how to go about this, and how cohost works in fundamental aspects. also has a working login thing which i need to properly understand at some point
  • cohost.org - home of eggbug
  • requests - i would be lost in python if it weren't for requests, my beloved
  • the random tumblr anon who sent me an activation link - thanks

cohost.py's People

Contributors

aitorres avatar crcrate avatar lunasorcery avatar oliviacrain avatar sakimori avatar theturfster avatar valknight avatar vallerieknight-unity avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

cohost.py's Issues

[Python 3.9 compatibility issue] TypeError: unsupported operand type(s) for |: 'types.GenericAlias' and 'type'

Running from cohost.models.user import User, I get this error:

  File "/home/jpl/.local/lib/python3.9/site-packages/cohost/models/user.py", line 6, in <module>
    from cohost.models.project import EditableProject
  File "/home/jpl/.local/lib/python3.9/site-packages/cohost/models/project.py", line 3, in <module>
    from cohost.models.block import AttachmentBlock
  File "/home/jpl/.local/lib/python3.9/site-packages/cohost/models/block.py", line 5, in <module>
    from cohost.network import fetch, generate_login_cookies
  File "/home/jpl/.local/lib/python3.9/site-packages/cohost/network.py", line 75, in <module>
    def fetchTrpc(methods: list[str] | str, cookie: str,
TypeError: unsupported operand type(s) for |: 'types.GenericAlias' and 'type'

I don't recognize this error. Python 3.9 is supported by this module, right?

TypeError: 'type' object is not subscriptable

trying to use the example.py after installing via pip complainst that 'type' object is not subscriptable. Not sure if I'm just holding it wrong or what...

Traceback (most recent call last):
File "post.py", line 2, in
from cohost.models.user import User
File "/home/jer/.local/lib/python3.8/site-packages/cohost/models/user.py", line 3, in
from cohost.models.project import EditableProject
File "/home/jer/.local/lib/python3.8/site-packages/cohost/models/project.py", line 90, in
class EditableProject(Project):
File "/home/jer/.local/lib/python3.8/site-packages/cohost/models/project.py", line 108, in EditableProject
def post(self, headline: str, blocks: list[Block], cws: list = [], tags: list = [], adult: bool = False, draft = False):
TypeError: 'type' object is not subscriptable

Unable to make posts with attachments

Starting a few days ago, my bot (pokemonoftheday) gives the following error when it gets run:

Traceback (most recent call last):
  File "/media/d4d6/countertenor/private/potd/potdcohost.py", line 107, in <module>
    newPost = project.post("Today's Pokémon is #" + str(pokeNum) + ": " + pokeName + ", the " + pokeGenus, blocks, tags=['pokemon', pokeName, 'automated posting', 'bot'])
  File "/media/d4d6/countertenor/python/lib/python3.9/site-packages/cohost/models/project.py", line 197, in post
    attachment.uploadIfNot(req['postId'], self)
  File "/media/d4d6/countertenor/python/lib/python3.9/site-packages/cohost/models/block.py", line 88, in uploadIfNot
    dospacecreds = fetch('post', endpoint, {
  File "/media/d4d6/countertenor/python/lib/python3.9/site-packages/cohost/network.py", line 58, in fetch
    raise Exception(res)
Exception: [{'type': 'Body', 'errors': {'issues': [{'code': 'invalid_type', 'expected': 'number', 'received': 'string', 'path': ['content_length'], 'message': 'Expected number, received string'}], 'name': 'ZodError'}}]

Based on the comments in https://cohost.org/ghobot/post/1361443-huh, this appears to be related to AttachmentBlock. Updating to 0.2.5 does not help.

Logins stopped working overnight

My code was working just fine last night, but today I'm getting some fun errors:

Traceback (most recent call last):
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/site-packages/requests/models.py", line 971, in json
    return complexjson.loads(self.text, **kwargs)
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/site-packages/cohost/network.py", line 48, in fetch
    res = req.json()
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/site-packages/requests/models.py", line 975, in json
    raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/root/cohostbot/bot.py", line 73, in <module>
    main()
  File "/root/cohostbot/bot.py", line 56, in main
    user = User.login(username, password)
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/site-packages/cohost/models/user.py", line 89, in login
    salt = fetch("GET", "/login/salt", {"email": email})['salt']
  File "/root/.pyenv/versions/3.10.4/lib/python3.10/site-packages/cohost/network.py", line 50, in fetch
    res = req.text()
TypeError: 'str' object is not callable

Here's the relevant code (with username and email stripped out). It worked fine last night and then stopped working without any changes.

        username = 'SECRET'
        password = 'NOTFORYOU'
        handle = 'StreetViewBot'
        user = User.login(username, password)
        project = user.getProject(handle)
        blocks = [
                AttachmentBlock('streetview_image/gsv_0.jpg'),
                MarkdownBlock('<div style="text-align: center; font-weight: bold;">'),
                MarkdownBlock(location.address),
                MarkdownBlock('</div>')
        ]
        tags = [
                'street view',
                'maps',
                location.raw['address']['country']
        ]
        newPost = project.post('', blocks,tags)
        print(f'Posted to {newPost.url}')

backports.pbkdf2?

any particular reason for requiring backports.pbkdf2 to provide pbkdf2_hmac rather than using hashlib.pbkdf2_hmac? Is there a subtle compatibility issue or something?

403 error if page is private, but post is still made

I get an exception and 403 error saying "Something went wrong." if I try to post to a private page, but the post still is made and visible on the cohost site.

from cohost.models.user import User
from cohost.models.block import AttachmentBlock, MarkdownBlock

import os
user = User.login(os.getenv("COHOST_USER"), os.getenv("COHOST_PASS"))
project = user.getProject('myPage')

blocks=[MarkdownBlock('test')]

newPost = project.post(headline="test", blocks=blocks) # this is where the exception is raised

print('Check out your post at {}'.format(newPost.url))

image

No clear error thrown when media larger than 10mb is uploaded

I have a bot that posts fairly large images (up to around 4 MB). First it ran fine, but over time more and more regularely project.post() fails with following error message, up to the point that it now fails every time:

Traceback (most recent call last):
  File "/home/me/.local/lib/python3.11/site-packages/cohost/network.py", line 61, in fetch
    res = req.json()
          ^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/requests/models.py", line 897, in json
    return complexjson.loads(self.text, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/me/SlideshowBot/bot.py", line 26, in <module>
    newPost = project.post(
              ^^^^^^^^^^^^^
  File "/home/me/.local/lib/python3.11/site-packages/cohost/models/project.py", line 226, in post
    attachment.uploadIfNot(req['postId'], self)
  File "/home/me/.local/lib/python3.11/site-packages/cohost/models/block.py", line 99, in uploadIfNot
    dospacecreds = fetch('postjson', endpoint, {
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/me/.local/lib/python3.11/site-packages/cohost/network.py", line 63, in fetch
    raise Exception(req.text)
Exception

The relevant code is

user = User.login(user, pw)
project = user.getProject(projectName)
blocks = [
    AttachmentBlock(imagePath),
]
newPost = project.post(
    "", blocks, tags=[tagList]
)

I have two other bots (one that posts small images and one that posts text only) that run without issues with basically the same code.

Notification history API appears to have changed on cohost's end

EDIT: ignore this, looks like they're rolling out some sorta revamped notification system so let's hold off until that's out fully?

Looks like the /notifications/list endpoint uses the query parameter before with a datetime for pagination now, and offset no longer seems to affect the returned results. I think it's been this way for a while.

Under the new scheme the request looks something like:
GET /api/v1/notifications/list?limit=40&before=2024-02-27T12:30:39.968Z

and the API response would look something like:

{
    "comments": {...},
    "posts": {...},
    "projects": {...},
    "notifications": [...],
    "nextBefore": "2024-02-26T23:40:04.858Z"
}

I can have a stab at making the necessary code changes if you'd like? It would mean a minor API break for scripts using the library...

Cleanup Base64 weirdness in User

Spawned from PR by @CrCrate - #1

Error when logging in ,specifically in decoding base64 - erorr is binascii.Error: Invalid base64-encoded string: number of data characters (21) cannot be 1 more than a multiple of 4

EDIT: This is now solved, but, I'm keeping the issue open to have a mental note to come back and clean this up at some point.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.