/clubhouse-py

Clubhouse API written in Python. Standalone client included. For reference and education purposes only.

Primary LanguagePythonMIT LicenseMIT

WARNING

  • FOR REFERENCE AND EDUCATION PURPOSES ONLY. THIS DOES NOT COME WITH ANY KINDS OF WARRANTY.

  • PLEASE DO NOT CREATE BOTS OR DO ANY HARMFUL THINGS TO THE SERVICE. DON'T BREAK THINGS. DON'T BE EVIL.

  • THIS PROJECT WILL DISCONTINUE DEVELOPMENT ONCE DEVELOPERS RELEASE AN ANDROID BUILD.

Pull Requests / Issues

I have disabled PRs and issues temporarily. I may/may not continue on development.

Closed PRs / Closed Issues

QnA

Are you affiliated with those guys who built the website that streamed Clubhouse rooms?

No.

I am not affiliated with anyone or any company.

This is just a personal project.

Why did you develop this? what is your whole intention about releasing this to public?

  1. There has been a lot of articles about security concerns of Clubhouse when I joined Clubhouse.
  2. I decided to take a closer look at the application by reverse engineering the app. With this I can possibly know what is the truth and what isn't.
  3. I found some possible security risks during the analysis. However, I will not disclose this information until things are properly and safely mitigated.
  4. I was planning to destroy my work, but I've decided to share the code as (i) I found out that the whole authentication flow and API base may change in the future, so this src will be priceless at some point of time (ii) I think it would be better off for Android phone users to interact with others. (iii) I wanted more people to join into conversations and have fun.

What if someone uses your code to do malicious activities? Wouldn't that be an issue?

  1. Evil people with evil intentions will do bad things even if the sourcecode wasn't released.
  2. There has been already numerous reports of trollers doing bad things around here and there. (Reference) These trollers have also disclosed their sourcecode so please have some time to check their source code. They did their own stuff without even referencing my source code. This already implies that evil people will always try to break stuff and do bad things.
  3. What I shared on GitHub is a very basic thing a reverse engineer could do. It's technically not difficult to get these information snatched from the binary.
  4. Clubhouse has a straightforward API with some unknown security mechanisms; They have implemented things to ban you for excessive usage.
  5. DO NOT even try anything if you don't really know what you'r trying to do. I have been mentioning the same message over here and there.
  6. I am not liable for anything you do with this application. I already warned about this as well.

You've released API keys and secret keys. Wouldn't that severely impact the server?

  1. Let me make things clear first. Those keys are NOT confidential secrets.
  2. These are just identifiers for third-party services to declare that your request is coming from the Clubhouse app.
  3. These keys are used for communication, adding your instagram/twitter accounts, chat notifications, etc.

Can you disclose what you've found during an analysis?

No. But I'm planning to send DMs / mails to Clubhouse employees in near future.

Then, can you explain a bit on that myth about Chinese IP thing?

  1. It's fixed in the latest version. You don't have to worry about this anymore.
  2. Worth reading this technical post for more detailed information.
  3. The blog post is written in Korean so you should probably translate the page.

I heard that the app is using iOS just to prevent the voice recording. Releasing these kinds of code can possibly make it 'easier' to make voice recording. I want to hear your opinions.

  1. if you have a "physical" recording device next to your mobile phone, there is literally no way to disallow users from recording the voice.
  2. If someone records and shares your voice record anonymously, there is no way to even catch or block the user.
  3. I think there are much more serious risks or problems that CH developers need to take a look at.

What do you think about the Clubhouse app? Is it secure enough? Can you rate their security levels?

From my personal perspective:

  1. API: well-made
  2. Notifications: lgtm.
  3. Interaction with voice protocol: meh, but it looks like they're trying to work on it.

Don't you think your actions were ethically wrong?

  1. I also heard that these issues were raised and discussed over several months in an open Clubhouse chatroom, and I guess I've clarified a lot of questions people had over the Clubhouse app.
  2. Somemone would've already done this if it wasn't me anyways. At least I gave some initiative to try with good wills and share details with you guys.

Do you have any plans to do further analysis if Clubhouse opens up a bug bounty programme?

Very unlikely.

Is Clubhouse actually working hard to fix all kinds of security stuff? I'm really worried.

Yes, but there are some reasons why developers are taking some time.

  1. They probably don't want to break things while updating. Developers also need time to fix and test their own code.
  2. It may take a few days to get their updates reviewed by Apple.
  3. They also need to some time to make "best moves" in order to efficiently fix issues.

What would you do if Clubhouse tries to hire you?

This doesn't make sense.

  1. I don't think Clubhouse team likes me. (Assumption)
  2. I see some people who were very uncomfortable about me speaking about these issues in the first place. One guy even kicked me out of his channel for talking about this topic. 3. I'm not an American citizen. It is always difficult to get US visas for foreigners. Also, I don't really prefer to work in US. (no offense here, just my preferences)

As a typical user, what do I need to be very careful about when using Clubhouse?

  1. As a speaker: Always assume that someone is recording your voice. Always think multiple times before you speak. Don't speak out confidential/personal stuff. I am not saying that the Clubhouse is recording your voice. There are chances that some trolls or reporters are trying to record multiple chatrooms.
  2. As a moderator: You need to be alert and make quick decisions to make your channel healthy. If someone says something weird or does something crazy, you need to make quick decisions. Move that speaker to audience or just kick the user out of the channel. Simple as that. Also, be aware that you have a lot of privileges. Do not give moderators to unknown people. Any moderator can destroy the channel.

Clubhouse API written in Python

clubhouse-py is originally developed for the sake of interoperability.

Standalone client is also created with very basic features, including but not limited to the audio-chat.

Please note that you may get a permanent ban for sending invalid API requests. Server's ratelimit and security mechanisms are quite strict.

Downloads

Check Releases. OSX(x86_64) may not be stable for use yet.

Demo

Please click the image to open a Youtube video demo.

Demo video

Requirements

  • Windows or OSX
  • Python 3.7 or higher

Installation

By pip

  1. Install by pip
$ pip3 install clubhouse-py
...
Successfully built clubhouse-py
Installing collected packages: clubhouse-py
Successfully installed clubhouse-py-304.0.1
  1. You need to install Agora SDK for voice communication. Refer to Agora-Python-SDK#installation.

Manual Installation

  1. Clone project
$ git clone https://github.com/stypr/clubhouse-py.git clubhouse
$ cd clubhouse
  1. You need to install dependencies first.
$ pip3 install -r requirements.txt
  1. You need to install Agora SDK for voice communication. Refer to Agora-Python-SDK#installation.

Usage

  • For calling APIs from other script
from clubhouse.clubhouse import Clubhouse

...

if __name__ == "__main__":
    clubhouse = Clubhouse()
  • For running a standalone client
$ python3 cli.py

Supported features

Pre-authentication

  • def start_phone_number_auth(self, phone_number):
  • def call_phone_number_auth(self, phone_number):
  • def resend_phone_number_auth(self, phone_number):
  • def complete_phone_number_auth(self, phone_number, verification_code):
  • def check_for_update(self, is_testflight=False):

Post-authentication

  • def get_release_notes(self):
  • def check_waitlist_status(self):
  • def add_email(self, email):
  • def update_photo(self, photo_filename):
  • def follow(self, user_id, user_ids=None, source=4, source_topic_id=None):
  • def unfollow(self, user_id):
  • def block(self, user_id):
  • def unblock(self, user_id):
  • def follow_multiple(self, user_ids, user_id=None, source=7, source_topic_id=None):
  • def follow_club(self, club_id, source_topic_id=None):
  • def unfollow_club(self, club_id, source_topic_id=None):
  • def update_follow_notifications(self, user_id, notification_type=2):
  • def get_suggested_follows_similar(self, user_id):
  • def get_suggested_follows_friends_only(self, club_id=None, upload_contacts=True, contacts=()):
  • def get_suggested_follows_all(self, in_onboarding=True, page_size=50, page=1):
  • def ignore_suggested_follow(self, user_id):
  • def get_event(self, event_id, user_ids=None, club_id=None, is_member_only=False, event_hashid=None, description=None, time_start_epoch=None, name=None):
  • def create_event(self, name, time_start_epoch, description, event_id=None, user_ids=(), club_id=None, is_member_only=False, event_hashid=None):
  • def edit_event(self, name, time_start_epoch, description, event_id=None, user_ids=(), club_id=None, is_member_only=False, event_hashid=None):
  • def delete_event(self, event_id, user_ids=None, club_id=None, is_member_only=False, event_hashid=None, description=None, time_start_epoch=None, name=None):
  • def get_events(self, is_filtered=True, page_size=25, page=1):
  • def get_club(self, club_id, source_topic_id=None):
  • def get_club_members(self, club_id, return_followers=False, return_members=True, page_size=50, page=1):
  • def get_settings(self):
  • def get_welcome_channel(self):
  • def hide_channel(self, channel, hide=True):
  • def join_channel(self, channel, attribution_source="feed"):
  • def leave_channel(self, channel):
  • def make_channel_public(self, channel, channel_id=None):
  • def make_channel_social(self, channel, channel_id=None):
  • def end_channel(self, channel, channel_id=None):
  • def make_moderator(self, channel, user_id):
  • def block_from_channel(self, channel, user_id):
  • def get_profile(self, user_id):
  • def me(self, return_blocked_ids=False, timezone_identifier="Asia/Tokyo", return_following_ids=False):
  • def get_following(self, user_id, page_size=50, page=1):
  • def get_followers(self, user_id, page_size=50, page=1):
  • def get_mutual_follows(self, user_id, page_size=50, page=1):
  • def get_all_topics(self):
  • def get_channels(self):
  • def get_channel(self, channel, channel_id=None):
  • def active_ping(self, channel):
  • def audience_reply(self, channel, raise_hands=True, unraise_hands=False):
  • def change_handraise_settings(self, channel, is_enabled=True, handraise_permission=1):
  • def update_skintone(self, skintone=1):
  • def get_notifications(self, page_size=20, page=1):
  • def get_actionable_notifications(self):
  • def get_online_friends(self):
  • def accept_speaker_invite(self, channel, user_id):
  • def reject_speaker_invite(self, channel, user_id):
  • def invite_speaker(self, channel, user_id):
  • def uninvite_speaker(self, channel, user_id):
  • def mute_speaker(self, channel, user_id):
  • def get_suggested_speakers(self, channel):
  • def create_channel(self, topic="", user_ids=(), is_private=False, is_social_mode=False):
  • def get_create_channel_targets(self):
  • def get_suggested_invites(self, club_id=None, upload_contacts=True, contacts=()):
  • def get_suggested_club_invites(self, upload_contacts=True, contacts=()):
  • def invite_to_app(self, name, phone_number, message=None):
  • def invite_from_waitlist(self, user_id):
  • def search_users(self, query, followers_only=False, following_only=False, cofollows_only=False):
  • def search_clubs(self, query, followers_only=False, following_only=False, cofollows_only=False):
  • def get_topic(self, topic_id):
  • def get_clubs_for_topic(self, topic_id, page_size=25, page=1):
  • def get_clubs(self, is_startable_only):
  • def get_users_for_topic(self, topic_id, page_size=25, page=1):
  • def invite_to_existing_channel(self, channel, user_id):
  • def update_username(self, username):
  • def update_name(self, name):
  • def update_twitter_username(self, username, twitter_token, twitter_secret):
  • def update_instagram_username(self, code):
  • def update_displayname(self, name):
  • def refresh_token(self, refresh_token):
  • def update_bio(self, bio):
  • def record_action_trails(self, action_trails=()):
  • def add_user_topic(self, club_id, topic_id):
  • def remove_user_topic(self, club_id, topic_id):
  • def report_incident(self, user_id, channel, incident_type, incident_description, email):
  • def reject_welcome_channel(self):
  • def update_channel_flags(self, channel, visibility, flag_title, unflag_title):
  • def ignore_actionable_notification(self, actionable_notification_id):
  • def invite_to_new_channel(self, user_id, channel):
  • def accept_new_channel_invite(self, channel_invite_id):
  • def reject_new_channel_invite(self, channel_invite_id):
  • def cancel_new_channel_invite(self, channel_invite_id):
  • def add_club_admin(self, club_id, user_id):
  • def remove_club_admin(self, club_id, user_id):
  • def remove_club_member(self, club_id, user_id):
  • def accept_club_member_invite(self, club_id, source_topic_id=None):
  • def add_club_member(self, club_id, user_id, name, phone_number, message, reason):
  • def get_club_nominations(self, club_id, source_topic_id):
  • def approve_club_nomination(self, club_id, source_topic_id, invite_nomination_id):
  • def reject_club_nomination(self, club_id, source_topic_id, invite_nomination_id):
  • def add_club_topic(self, club_id, topic_id):
  • def remove_club_topic(self, club_id, topic_id):
  • def get_events_to_start(self):
  • def update_is_follow_allowed(self, club_id, is_follow_allowed=True):
  • def update_is_membership_private(self, club_id, is_membership_private):
  • def update_is_community(self, club_id, is_community):
  • def update_club_description(self, club_id, description):

Unsupported features

Endpoints

  • def update_club_rules(self):
  • def update_club_topics(self):
  • def get_events_for_user(self):

PubNub

PubNub is used for the notification while being in a conversation. This has not been implemented yet. However, you may utilize the PubSub keys provided in the sourcecode to implement this.

Reference / Recommended to read

You may also add more endpoints and features based on the following repositories.

Please note that these repositories were partially referenced to create this project.

Most of things were tested and handcrafted from scratch.