GroupMe Push Client
A client for the GroupMe push service (Faye). It opens a websocket to GroupMe and calls your functions when messages, DMs and likes arrive.
Installation
pip install groupme-push, or clone the repo and run pip install .
Quick start
from groupme_push import PushClient
def on_message(message):
print(message["text"])
client = PushClient(access_token="useraccesstoken", on_message=on_message)
client.start()
client.join() # block until the client stops
Get an access token from dev.groupme.com.
Listening to one group
GroupMe pushes every group's messages down your personal channel, so subscribe_to_group() does not narrow the stream — it adds a group's own channel (typing notifications and the like) on top. To hear about only certain groups, pass group_ids:
client = PushClient(
access_token="useraccesstoken",
on_message=on_message,
group_ids=["12345678"], # everything else is dropped
)
client.start()
DMs and other events that have no group id are never filtered out; leave on_dm unset if you do not want them.
To find your group ids:
for group_id, name in PushClient(access_token="useraccesstoken").list_groups():
print(group_id, name)
Options
| Argument | Description |
|---|---|
access_token |
GroupMe access token for the user you want to listen as. |
on_message |
Called with the message subject for every group message. |
on_dm |
Called for direct messages. |
on_like |
Called when another user likes a message. |
on_favorite |
Called when your user likes a message. |
on_other |
Called with the raw data blob for anything else, such as poll results. |
on_connect |
Called with no arguments each time the socket becomes ready, including after a reconnect. |
on_error |
Called with the exception when the websocket errors. |
disregard_self |
Skip events sent by the authenticated user. Default False. |
reconnect |
Seconds to wait before reconnecting after a dropped connection. Default 5. None disables reconnection, which means any drop is permanent. |
ping_interval / ping_timeout |
Websocket keepalive. Defaults 30 / 10. See below. |
stall_timeout |
Seconds of silence after which the client checks that the stream is still alive. Default 180. None disables the check. |
probe_timeout |
How long that check waits for an answer. Default 10. |
subscribe_timeout |
Seconds to wait for a connection's subscriptions to be accepted before rebuilding the session. Default 15. None disables the check. |
group_ids |
Only dispatch events from these groups. Default: no filtering. |
subscribe_to_user_channel |
Subscribe to /user/<id> on connect. This is where GroupMe delivers messages, so leave it on unless you know otherwise. Default True. |
threaded_callbacks |
Run each callback in its own thread. Set False to run them inline, which keeps events in order but blocks the socket while they run. Default True. |
request_timeout |
Timeout in seconds for the HTTP calls start() makes. Default 5. |
Staying connected
A GroupMe push connection does not stay up on its own. An idle one is closed by the server, and the Faye session behind it gets reaped, after which the socket can still look perfectly healthy while never delivering anything again. That is the "it ran fine all day and then just stopped receiving" failure.
The client handles this out of the box:
- Websocket pings (
ping_interval) keep the connection warm and, more importantly, make a dead connection raise instead of hanging. Without them the reader blocks on a socket that will never produce another byte, which is what a sleeping laptop or a NAT timeout leaves behind. - Automatic reconnection is on by default (
reconnect=5). Subscriptions are replayed on the new connection. - A liveness check (
stall_timeout). GroupMe sends nothing at all down an idle connection, so silence proves nothing on its own. After a quiet stretch the client asks a question instead of guessing: it re-subscribes to a channel it already holds, which is the one request GroupMe reliably answers. An answer means the stream is fine, an error means the session was reaped and it rebuilds, and no answer at all means the socket is gone and it reconnects. - A subscription check (
subscribe_timeout). Being connected is not the same as being subscribed. If nothing confirms a connection's subscriptions, the session is rebuilt rather than left silently listening to nothing. - A fresh session on reconnect. Reusing a Faye session across a dropped socket is unreliable — the server can keep delivering to the connection that just died — so a reconnect takes a new one. Rebuilding is retried with backoff, because a network that is down when the client notices is usually still down a second later.
If you would rather do your own supervision, set reconnect=None, stall_timeout=None and subscribe_timeout=None.
Methods
start(wait=False, timeout=10)— authenticate, handshake and open the socket. Withwait=Trueit blocks until the socket is open and raisesConnectionTimeoutif it never does.stop(timeout=5)— close the socket and stop reconnecting.join(timeout=None)— block the calling thread until the client stops.wait_until_connected(timeout=10)/is_connected— connection state.subscribe_to_group(group_id, timeout=10)— subscribe to a group's own channel. Safe to call right afterstart(): it waits for the socket, and the subscription is replayed automatically after a reconnect.unsubscribe_from_group(group_id)— the reverse.list_groups(per_page=100)—[(group_id, name), ...]for the user's groups, over REST. Handy for finding the ids to pass togroup_ids.
PushClient is also a context manager:
with PushClient(access_token="useraccesstoken", on_message=on_message) as client:
client.join()
Registering handlers with decorators
client = PushClient(access_token="useraccesstoken")
@client.receive_message
def on_message(message):
print(message["text"])
There is one of these per callback: receive_message, receive_dm, receive_like, receive_favorite, receive_other, receive_connect, receive_error.
Errors
start() raises instead of logging and carrying on with a dead client:
AuthenticationError— GroupMe rejected the access token.HandshakeError— Faye did not return a client id.ConnectionTimeout—start(wait=True)timed out.
All of them subclass GroupMePushError.
Examples
See examples/: basic.py, single_group.py, decorators.py.
Development
pip install -e ".[dev]"
pytest
The tests run against a scripted fake websocket, so no token or network access is needed.
Releases are additionally smoke tested against the live GroupMe service: the client creates a throwaway group, posts to it, and asserts on delivery, ordering, filtering, disregard_self and reconnect recovery.
Issues
If you encounter any bugs or have feature requests, please open an issue on GitHub.
Metadata
Release files for groupme-push 0.0.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| groupme_push-0.0.6.tar.gz | 37.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| groupme_push-0.0.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.4 kB
Release files / groupme_push-0.0.6.tar.gz
| Download URL | groupme_push-0.0.6.tar.gz |
|---|---|
| Size | 37.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e6d7f753173a371c7a0a95b1d47f54531afdef8b98efa18312cef7679553c75
|
|
BLAKE2b-256 checksum How to use checksums |
573cf4d52cd4f49877da6d9512fe7fac24889ccc903445beac0812cd22df1669
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|
Release files / groupme_push-0.0.6-py3-none-any.whl
| Download URL | groupme_push-0.0.6-py3-none-any.whl |
|---|---|
| Size | 26.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e79f275b3b096b03697f0e827268fb1379be8ea34a5b1672ec9d8ee4b4601c74
|
|
BLAKE2b-256 checksum How to use checksums |
3cceabeff123774d1a93f4a082ff630d583353eda068fa21f28c380536070172
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|