### Install Signalbot with Examples Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Install the Signalbot library with the necessary dependencies for running the examples. ```bash pip install signalbot[examples] ``` -------------------------------- ### Install Signalbot with Example Dependencies Source: https://github.com/signalbot-org/signalbot/blob/main/docs/local_development.md Installs signalbot and its example dependencies using uv. Ensure you have a virtual environment set up. ```bash uv sync --groups examples ``` -------------------------------- ### Install Documentation Dependencies Source: https://github.com/signalbot-org/signalbot/blob/main/docs/local_development.md Installs the necessary dependencies for serving the documentation locally using uv. ```bash uv sync --group docs ``` -------------------------------- ### Minimal Signal Bot Example Source: https://github.com/signalbot-org/signalbot/blob/main/README.md This snippet demonstrates the basic structure of a Signal bot. It includes setting up logging, configuring the bot with Signal service and phone number, registering a simple 'Ping' command, and starting the bot. Ensure SIGNAL_SERVICE and PHONE_NUMBER environment variables are set. ```python import os import logging from signalbot import SignalBot, Config, Command, Context, triggered, enable_console_logging class PingCommand(Command): @triggered("Ping") async def handle(self, context: Context) -> None: await context.send("Pong") if __name__ == "__main__": enable_console_logging(logging.INFO) bot = SignalBot( Config( signal_service=os.environ["SIGNAL_SERVICE"], phone_number=os.environ["PHONE_NUMBER"], ) ) bot.register(PingCommand()) # Run the command for all contacts and groups bot.start() ``` -------------------------------- ### Minimal Signal Bot Example Source: https://github.com/signalbot-org/signalbot/blob/main/docs/index.md This is a basic example demonstrating the core functionality of a Signal bot using the package. It serves as a starting point for developing more complex bots. ```python --8<-- "examples/simple_bot.py" ``` -------------------------------- ### Run signal-cli-rest-api in Normal Mode Source: https://github.com/signalbot-org/signalbot/blob/main/docs/getting_started.md Use this command to start the signal-cli-rest-api Docker container in normal mode for initial setup and QR code linking. Ensure the configuration directory is mounted. ```bash docker run -p 8080:8080 \ -v $(pwd)/signal-cli-config:/home/.local/share/signal-cli \ -e 'MODE=normal' bbernhard/signal-cli-rest-api:latest ``` -------------------------------- ### Install Pre-commit Hooks Source: https://github.com/signalbot-org/signalbot/blob/main/docs/local_development.md Installs the pre-commit hooks for linting and formatting using uv. This ensures code quality and consistency. ```bash uv run prek install ``` -------------------------------- ### Initialize and Start a SignalBot with a Ping Command Source: https://context7.com/signalbot-org/signalbot/llms.txt Demonstrates how to initialize the SignalBot with configuration, register a simple PingCommand, and start the bot. The PingCommand responds to the exact text 'Ping' with 'Pong'. Commands can be registered for all contacts/groups, only groups, or specific contacts/groups. ```python import os import logging from signalbot import SignalBot, Config, Command, Context, triggered, enable_console_logging enable_console_logging(logging.INFO) class PingCommand(Command): @triggered("Ping") async def handle(self, context: Context) -> None: await context.send("Pong") bot = SignalBot( Config( signal_service=os.environ["SIGNAL_SERVICE"], # e.g. "127.0.0.1:8080" phone_number=os.environ["PHONE_NUMBER"], # e.g. "+49123456789" ) ) bot.register(PingCommand()) # active for all contacts + groups bot.register(PingCommand(), contacts=False, # groups only groups=["Ops Team"]) bot.register(PingCommand(), contacts=["+49111222333"],# specific contact only groups=False) bot.start() ``` -------------------------------- ### Implement a Stateful Command with Message Counting Source: https://context7.com/signalbot-org/signalbot/llms.txt Illustrates creating a custom command by subclassing `Command`. The `setup` method is used for one-time initialization, and the `handle` method processes incoming messages, maintaining a count of messages sent by each user using the bot's persistent storage. ```python from signalbot import Command, Context class StatefulCommand(Command): def setup(self) -> None: # Called once on registration; bot.groups not yet available self.call_count = 0 async def handle(self, context: Context) -> None: self.call_count += 1 # Access persistent storage via self.bot.storage key = f"calls:{context.message.source}" if self.bot.storage.exists(key): total = self.bot.storage.read(key) + 1 else: total = 1 self.bot.storage.save(key, total) await context.send(f"You have sent {total} messages total.") ``` -------------------------------- ### Install Signalbot Source: https://github.com/signalbot-org/signalbot/blob/main/docs/getting_started.md Install the signalbot Python package using pip. It's recommended to do this within a virtual environment. ```bash pip install signalbot ``` -------------------------------- ### Basic Signalbot Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/getting_started.md This is a simple example bot script that can be run after setting up signal-cli-rest-api. It requires specific environment variables for connection. ```python from signalbot import SignalBot bot = SignalBot() @bot.command() def ping(message): return "Pong" bot.run() ``` -------------------------------- ### Set Environment Variables and Run Bot Source: https://github.com/signalbot-org/signalbot/blob/main/docs/getting_started.md Configure the necessary environment variables for SIGNAL_SERVICE and PHONE_NUMBER before running the bot script. This example assumes a local signal-cli-rest-api instance. ```bash export SIGNAL_SERVICE="127.0.0.1:8080" export PHONE_NUMBER="+49123456789" python bot.py ``` -------------------------------- ### Configuring Console Logging Source: https://context7.com/signalbot-org/signalbot/llms.txt Enables console logging for the signalbot library by attaching a `StreamHandler`. Call `enable_console_logging()` before starting the bot to view internal log messages. Supports different logging levels like DEBUG, INFO, WARNING, and ERROR. ```python import logging from signalbot import enable_console_logging enable_console_logging(logging.DEBUG) # DEBUG | INFO | WARNING | ERROR # Output format: # 2024-01-15 12:00:01 signalbot [INFO] - _produce - [Bot] Producer #1 started ``` -------------------------------- ### Signalbot Producer and Consumer Logs Source: https://github.com/signalbot-org/signalbot/blob/main/docs/getting_started.md These logs indicate that the Signalbot producer and consumers have started successfully. The producer monitors for new messages, and consumers process tasks for registered commands. ```text signalbot [WARNING] - __init__ - [Bot] Could not initialize Redis and no SQLite DB name was given. In-memory storage will be used. Restarting will delete the storage! Add storage: {'type': 'in-memory'} to the config to silence this error. signalbot [INFO] - _detect_groups - [Bot] 3 groups detected signalbot [INFO] - _produce - [Bot] Producer #1 started signalbot [INFO] - _consume - [Bot] Consumer #1 started signalbot [INFO] - _consume - [Bot] Consumer #2 started signalbot [INFO] - _consume - [Bot] Consumer #3 started ``` -------------------------------- ### Serve Documentation Locally Source: https://github.com/signalbot-org/signalbot/blob/main/docs/local_development.md Runs the mkdocs serve command to serve the documentation locally with live reloading. The --watch flag monitors the current directory for changes. ```bash uv run mkdocs serve --livereload --watch ./ ``` -------------------------------- ### Configure Bot with JSON File Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_config_options.md Initialize the bot using a JSON configuration file. The file must include 'signal_service' and 'phone_number'. This method is convenient for structured configuration. ```json { "signal_service": "http://localhost:8080", "phone_number": "+1234567890", } ``` ```python from signalbot import SignalBot bot = SignalBot("config.json") bot.start() ``` -------------------------------- ### Help Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command to provide help information to users. ```python --8<-- "examples/commands/help.py" ``` -------------------------------- ### Configure SignalBot using different sources Source: https://context7.com/signalbot-org/signalbot/llms.txt Shows how to initialize SignalBot's Config using a Config object, a plain dictionary, or a YAML file path. This allows flexibility in how bot settings like signal service address, phone number, and storage backend are provided. ```python from signalbot import SignalBot, Config, ConnectionMode # From Config object bot = SignalBot(Config( signal_service="127.0.0.1:8080", phone_number="+49123456789", download_attachments=True, retry_interval=5, connection_mode=ConnectionMode.AUTO, # AUTO | HTTPS_ONLY | HTTP_ONLY storage={"type": "sqlite", "sqlite_db": "./bot.db"}, )) # From plain dict bot = SignalBot({ "signal_service": "127.0.0.1:8080", "phone_number": "+49123456789", "storage": {"type": "in-memory"}, }) # From YAML file # config.yml: # signal_service: "127.0.0.1:8080" # phone_number: "+49123456789" # storage: # type: redis # redis_host: localhost # redis_port: 6379 bot = SignalBot("config.yml") bot.start() ``` -------------------------------- ### Configure Bot with YAML File Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_config_options.md Load bot configuration from a YAML file. Ensure the file contains 'signal_service' and 'phone_number' keys. This is useful for externalizing configuration. ```yaml signal_service: "http://localhost:8080" phone_number: "+1234567890" ``` ```python from signalbot import SignalBot bot = SignalBot("config.yml") bot.start() ``` -------------------------------- ### Configure Bot with Config Object Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_config_options.md Instantiate a Config object with service and phone number details, then pass it to SignalBot. Ensure the Signal service is accessible. ```python from signalbot import SignalBot, Config, ConnectionMode config = Config( signal_service="http://localhost:8080", phone_number="+1234567890", ) bot = SignalBot(config) bot.start() ``` -------------------------------- ### Configure Bot with Dictionary Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_config_options.md Use a Python dictionary to define configuration parameters like signal_service and phone_number before initializing SignalBot. This method is suitable for dynamic configuration. ```python from signalbot import SignalBot, Config config = { "signal_service": "http://localhost:8080", "phone_number": "+1234567890", } bot = SignalBot(config) bot.start() ``` -------------------------------- ### Styles Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command to demonstrate different text styling options in Signal messages. ```python --8<-- "examples/commands/styles.py" ``` -------------------------------- ### Typing Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command to simulate the typing indicator in Signal. ```python --8<-- "examples/commands/typing.py" ``` -------------------------------- ### Configure Redis Storage Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_config_options.md Set up Redis as the storage backend for the bot. Provide 'redis_host' and 'redis_port' for connecting to the Redis instance. This enables distributed data persistence. ```yaml signal_service: "http://localhost:8080" phone_number: "+1234567890" storage: type: "redis" redis_host: "localhost" redis_port: 6379 ``` -------------------------------- ### Ping Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a simple command that responds to a 'ping' request. ```python --8<-- "examples/commands/ping.py" ``` -------------------------------- ### Reply Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command that allows the bot to reply to messages. ```python --8<-- "examples/commands/reply.py" ``` -------------------------------- ### Configure SQLite Storage Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_config_options.md Configure the bot to use SQLite for data persistence. Specify the 'sqlite_db' path for the database file. This provides local file-based storage. ```yaml signal_service: "http://localhost:8080" phone_number: "+1234567890" storage: type: "sqlite" sqlite_db: "./data/bot.db" ``` -------------------------------- ### Persistent Key-Value Storage with bot.storage Source: https://context7.com/signalbot-org/signalbot/llms.txt Demonstrates using bot.storage for persistent key-value operations like checking existence, reading, saving, and deleting data. Values are automatically JSON-serialized. Use this for managing state across bot restarts. ```python from signalbot import Command, Context, triggered class CounterCommand(Command): KEY = "global_counter" @triggered("count") async def handle(self, context: Context) -> None: storage = self.bot.storage if storage.exists(self.KEY): count = storage.read(self.KEY) + 1 else: count = 1 storage.save(self.KEY, count) await context.send(f"Counter: {count}") @triggered("reset") async def reset(self, context: Context) -> None: self.bot.storage.delete(self.KEY) await context.send("Counter reset.") ``` -------------------------------- ### Unit Testing with ChatTestCase and mock_chat Source: https://context7.com/signalbot-org/signalbot/llms.txt Provides utilities for unit testing Signal bots. `ChatTestCase` sets up an in-memory environment, and `@mock_chat` injects mock messages to simulate incoming traffic. Use this to test command logic without a live Signal connection. ```python import pytest from pytest_mock import MockerFixture from signalbot.utils import ChatTestCase, mock_chat from signalbot import Command, Context, triggered class EchoCommand(Command): @triggered("echo") async def handle(self, context: Context) -> None: await context.send(context.message.text.upper()) class TestEchoCommand(ChatTestCase): @pytest.fixture(autouse=True) def setup(self): super().setup() self.signal_bot.register(EchoCommand()) @pytest.mark.asyncio @mock_chat("echo") async def test_echo(self, mocker: MockerFixture): replies = self.signal_bot._signal.send assert replies.call_count == 1 for recipient, message in replies.results(): assert recipient == ChatTestCase.group_id assert message == "ECHO" @pytest.mark.asyncio @mock_chat("hello", "echo", "world") # only "echo" triggers the command async def test_only_one_reply(self, mocker: MockerFixture): assert self.signal_bot._signal.send.call_count == 1 ``` -------------------------------- ### Run Unit Tests Source: https://github.com/signalbot-org/signalbot/blob/main/docs/local_development.md Executes the unit tests for the project using uv and pytest. Ensure all tests pass after making changes. ```bash uv run pytest ``` -------------------------------- ### Multiple Triggered Commands Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command that can be triggered by multiple different phrases. ```python --8<-- "examples/commands/multiple_triggered.py" ``` -------------------------------- ### Python Bot with Scheduler Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_with_scheduler.md This Python script shows how to set up a Signal bot that utilizes a scheduler for automated tasks, bypassing the standard Command class for direct library interaction. ```python from signalbot import SignalBot from signalbot.scheduler import Scheduler import asyncio import logging logging.basicConfig(level=logging.INFO) def send_message(bot: SignalBot, recipient: str, message: str): asyncio.run_coroutine_threadsafe(bot.send_message(recipient, message), bot.loop) def main(): bot = SignalBot(number='+1234567890', account_key='account.key') scheduler = Scheduler(bot) # Schedule a message to be sent every 5 seconds scheduler.schedule_interval(send_message, 5, bot=bot, recipient='+1098765432', message='Hello from the scheduler!') # Schedule a message to be sent once after 10 seconds scheduler.schedule_once(send_message, 10, bot=bot, recipient='+1098765432', message='This is a one-time message.') # Start the bot and the scheduler bot.start() scheduler.start() # Keep the bot running bot.loop.run_forever() if __name__ == '__main__': main() ``` -------------------------------- ### Reaction Command Implementations Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement commands to add reactions to messages, including a specific command for thumbs up. ```python --8<-- "examples/commands/reaction.py" ``` -------------------------------- ### Run signal-cli-rest-api in JSON-RPC Mode Source: https://github.com/signalbot-org/signalbot/blob/main/docs/getting_started.md After linking your account, restart the signal-cli-rest-api Docker container in json-rpc mode for bot communication. The access key is stored in the mounted volume. ```bash docker run -p 8080:8080 \ -v $(pwd)/signal-cli-config:/home/.local/share/signal-cli \ -e 'MODE=json-rpc' bbernhard/signal-cli-rest-api:latest ``` -------------------------------- ### Attachment Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command to handle attachments in Signal messages. ```python --8<-- "examples/commands/attachments.py" ``` -------------------------------- ### Context.react() Source: https://context7.com/signalbot-org/signalbot/llms.txt Sends an emoji reaction on the message that triggered the command. This allows for quick acknowledgments or responses to incoming messages. ```APIDOC ## `Context.react()` — react to the incoming message Sends an emoji reaction on the message that triggered the command. ```python from signalbot import Command, Context, triggered class AckCommand(Command): @triggered("ack") async def handle(self, context: Context) -> None: await context.react("✅") ``` ``` -------------------------------- ### Create a Signal Poll with bot.poll() Source: https://context7.com/signalbot-org/signalbot/llms.txt Creates a native Signal poll in a group or direct chat. Requires receiver, question, and a list of answers. ```python from signalbot import SignalBot async def run_poll(bot: SignalBot) -> None: await bot.init_task timestamp = await bot.poll( receiver="My Group", question="What should we have for lunch?", answers=["Pizza", "Sushi", "Tacos"], allow_multiple_selections=False, ) print(f"Poll created at timestamp {timestamp}") ``` -------------------------------- ### React to Incoming Message with Context.react() Source: https://context7.com/signalbot-org/signalbot/llms.txt Sends an emoji reaction on the message that triggered the command. This is useful for acknowledging received messages. ```python from signalbot import Command, Context, triggered class AckCommand(Command): @triggered("ack") async def handle(self, context: Context) -> None: await context.react("✅") ``` -------------------------------- ### Main Bot Code Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md This is the main bot script that integrates various command handlers. ```python --8<-- "examples/bot.py" ``` -------------------------------- ### Regex Triggered Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command that is triggered by a regular expression pattern. ```python --8<-- "examples/commands/regex_triggered.py" ``` -------------------------------- ### Context.start_typing() / Context.stop_typing() Source: https://context7.com/signalbot-org/signalbot/llms.txt Shows or hides the typing indicator in the conversation. This can be used to provide visual feedback to the user that the bot is processing a request. ```APIDOC ## `Context.start_typing()` / `Context.stop_typing()` — typing indicator Shows or hides the typing indicator in the conversation. ```python import asyncio from signalbot import Command, Context, triggered class SlowCommand(Command): @triggered("slow") async def handle(self, context: Context) -> None: await context.start_typing() await asyncio.sleep(3) # simulate heavy computation await context.stop_typing() await context.send("Done processing!") ``` ``` -------------------------------- ### Schedule Jobs with bot.scheduler (APScheduler) Source: https://context7.com/signalbot-org/signalbot/llms.txt Integrates with APScheduler to schedule date, interval, or cron jobs. Jobs sending messages must await bot.init_task. ```python import logging import os from signalbot import SignalBot, enable_console_logging enable_console_logging(logging.INFO) bot = SignalBot({ "signal_service": os.environ["SIGNAL_SERVICE"], "phone_number": os.environ["PHONE_NUMBER"], }) async def send_daily_summary(bot: SignalBot, receiver: str) -> None: await bot.init_task await bot.send(receiver, "Daily summary: all systems operational ✅") # Run once immediately, then every 24 hours bot.scheduler.add_job( send_daily_summary, args=[bot, os.environ["PHONE_NUMBER"]], trigger="interval", hours=24, ) bot.start() ``` -------------------------------- ### Inspecting Incoming Message Metadata Source: https://context7.com/signalbot-org/signalbot/llms.txt Shows how to access and inspect metadata of an incoming Signal message using the `context.message` object. Useful for commands that need to understand the context of a received message, like its type, sender, or content. ```python from signalbot import Command, Context, MessageType class InspectCommand(Command): async def handle(self, context: Context) -> None: msg = context.message info = [ f"source: {msg.source}", f"type: {msg.type}", f"text: {msg.text}", f"group: {msg.group}", f"is_private: {msg.is_private()}", f"is_group: {msg.is_group()}", f"has_attachments: {len(msg.base64_attachments) > 0}", f"has_quote: {msg.quote is not None}", f"has_reaction: {msg.reaction is not None}", ] if msg.type == MessageType.EDIT_MESSAGE: info.append(f"edits timestamp: {msg.target_sent_timestamp}") if msg.type == MessageType.DELETE_MESSAGE: info.append(f"deletes timestamp: {msg.remote_delete_timestamp}") await context.send("\n".join(info)) ``` -------------------------------- ### Delete Command Implementations Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement commands for deleting messages and local attachments, including handling delete commands received from Signal. ```python --8<-- "examples/commands/delete.py" ``` -------------------------------- ### Add Signalbot as Editable Dependency Source: https://github.com/signalbot-org/signalbot/blob/main/docs/local_development.md Adds the local signalbot repository as an editable dependency in another project using uv. This is useful when developing signalbot alongside your bot. ```bash uv add --editable ../signalbot ``` -------------------------------- ### Configure In-Memory Storage Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/bot_config_options.md Set the storage type to 'in-memory' in the configuration. This is suitable for development and testing as data is not persisted across restarts. ```yaml signal_service: "http://localhost:8080" phone_number: "+1234567890" storage: type: "in-memory" ``` -------------------------------- ### Show Typing Indicator with Context.start_typing() / Context.stop_typing() Source: https://context7.com/signalbot-org/signalbot/llms.txt Shows or hides the typing indicator in the conversation. Use this to provide feedback during long-running operations. ```python import asyncio from signalbot import Command, Context, triggered class SlowCommand(Command): @triggered("slow") async def handle(self, context: Context) -> None: await context.start_typing() await asyncio.sleep(3) # simulate heavy computation await context.stop_typing() await context.send("Done processing!") ``` -------------------------------- ### Context.send() - Sending Various Message Types Source: https://context7.com/signalbot-org/signalbot/llms.txt Send messages back to the chat, optionally including styled text, base64 attachments, link previews, or setting messages to be view-once. ```python import base64 from signalbot import Command, Context, triggered, LinkPreview class RichSendCommand(Command): @triggered("demo") async def handle(self, context: Context) -> None: # Plain text await context.send("Hello!") # Styled text (bold / italic / spoiler / monospace / strikethrough) await context.send("**Bold** and *italic*", text_mode="styled") # With a base64-encoded image attachment with open("photo.jpg", "rb") as f: b64 = base64.b64encode(f.read()).decode() await context.send("Here is a photo:", base64_attachments=[b64]) # With a link preview preview = LinkPreview( base64_thumbnail=None, title="Signalbot on GitHub", description="Python Signal bot framework", url="https://github.com/signalbot-org/signalbot", ) await context.send("Check this out:", link_preview=preview) # View-once message await context.send("This self-destructs after viewing.", view_once=True) ``` -------------------------------- ### bot.scheduler Source: https://context7.com/signalbot-org/signalbot/llms.txt Provides integration with APScheduler, allowing for the scheduling of jobs such as sending messages at specific times or intervals. Jobs that send messages must wait for `bot.init_task`. ```APIDOC ## `bot.scheduler` — APScheduler integration `bot.scheduler` exposes the underlying `AsyncIOScheduler` (APScheduler), allowing date, interval, or cron jobs to be scheduled. Jobs that need to send messages must wait for `bot.init_task` before calling any bot method. ```python import logging import os from signalbot import SignalBot, enable_console_logging enable_console_logging(logging.INFO) bot = SignalBot({ "signal_service": os.environ["SIGNAL_SERVICE"], "phone_number": os.environ["PHONE_NUMBER"], }) async def send_daily_summary(bot: SignalBot, receiver: str) -> None: await bot.init_task await bot.send(receiver, "Daily summary: all systems operational ✅") # Run once immediately, then every 24 hours bot.scheduler.add_job( send_daily_summary, args=[bot, os.environ["PHONE_NUMBER"]], trigger="interval", hours=24, ) bot.start() ``` ``` -------------------------------- ### `Context.reply()` Source: https://context7.com/signalbot-org/signalbot/llms.txt Replies to a message by quoting the original message, similar to `Context.send()` but automatically threads the response. ```APIDOC ## `Context.reply()` — reply quoting the original message Like `Context.send()` but automatically threads the response as a quoted reply to the message that triggered the command. ```python from signalbot import Command, Context, triggered class QuoteReplyCommand(Command): @triggered("quote me") async def handle(self, context: Context) -> None: # Sends a reply that quotes context.message await context.reply("You said: " + context.message.text) ``` ``` -------------------------------- ### Edit Command Implementation Source: https://github.com/signalbot-org/signalbot/blob/main/docs/examples/api_overview.md Implement a command to edit existing Signal messages. ```python --8<-- "examples/commands/edit.py" ``` -------------------------------- ### Send Read Receipt with bot.receipt() Source: https://context7.com/signalbot-org/signalbot/llms.txt Marks a direct (non-group) message as read or viewed. This is typically used for private messages. ```python from signalbot import Command, Context, triggered class ReadReceiptCommand(Command): async def handle(self, context: Context) -> None: # Mark private messages as read immediately if context.message.is_private(): await context.receipt("read") ``` -------------------------------- ### Signalbot Message Handling Logs Source: https://github.com/signalbot-org/signalbot/blob/main/docs/getting_started.md These logs show the flow of a message: receiving a raw message, a consumer processing it, and the bot sending a response. This confirms the bot is operational and responding to commands. ```text signalbot [INFO] - _produce - [Raw Message] {"envelope": } signalbot [INFO] - _consume_new_item - [Bot] Consumer #2 got new job in 0.00046 seconds signalbot [INFO] - _produce - [Raw Message] {"envelope": } signalbot [INFO] - send - [Bot] New message 1760797696983 sent: Pong ``` -------------------------------- ### bot.poll() Source: https://context7.com/signalbot-org/signalbot/llms.txt Creates a native Signal poll in a group or direct chat. This method allows users to create interactive polls with customizable questions and answers. ```APIDOC ## `bot.poll()` — create a Signal poll Creates a native Signal poll in a group or direct chat. ```python from signalbot import SignalBot async def run_poll(bot: SignalBot) -> None: await bot.init_task timestamp = await bot.poll( receiver="My Group", question="What should we have for lunch?", answers=["Pizza", "Sushi", "Tacos"], allow_multiple_selections=False, ) print(f"Poll created at timestamp {timestamp}") ``` ``` -------------------------------- ### Update Group Metadata with bot.update_group() Source: https://context7.com/signalbot-org/signalbot/llms.txt Updates a Signal group's name, description, avatar, or expiration timer. Requires group ID and desired metadata. Avatar must be base64 encoded. ```python import base64 async def configure_group(bot, group_id: str) -> None: with open("avatar.png", "rb") as f: b64_avatar = base64.b64encode(f.read()).decode() await bot.update_group( group_id=group_id, name="Ops Team", description="Operations channel", expiration_in_seconds=604800, # 1 week base64_avatar=b64_avatar, ) ``` -------------------------------- ### bot.receipt() Source: https://context7.com/signalbot-org/signalbot/llms.txt Sends a read or viewed receipt for a direct (non-group) message. This is used to indicate that a message has been seen by the recipient. ```APIDOC ## `bot.receipt()` — send a read/viewed receipt Marks a direct (non-group) message as read or viewed. ```python from signalbot import Command, Context, triggered class ReadReceiptCommand(Command): async def handle(self, context: Context) -> None: # Mark private messages as read immediately if context.message.is_private(): await context.receipt("read") ``` ``` -------------------------------- ### `Context.send()` Source: https://context7.com/signalbot-org/signalbot/llms.txt Sends a text message back to the chat where the triggering message originated. Supports attachments, link previews, mentions, and rich text styling. ```APIDOC ## `Context.send()` — send a message to the current chat Sends a text message back to wherever the triggering message came from (group or individual). Optionally attach base64-encoded files, link previews, mentions, or rich text styling. Returns the timestamp of the sent message. ```python import base64 from signalbot import Command, Context, triggered, LinkPreview class RichSendCommand(Command): @triggered("demo") async def handle(self, context: Context) -> None: # Plain text await context.send("Hello!") # Styled text (bold / italic / spoiler / monospace / strikethrough) await context.send("**Bold** and *italic*", text_mode="styled") # With a base64-encoded image attachment with open("photo.jpg", "rb") as f: b64 = base64.b64encode(f.read()).decode() await context.send("Here is a photo:", base64_attachments=[b64]) # With a link preview preview = LinkPreview( base64_thumbnail=None, title="Signalbot on GitHub", description="Python Signal bot framework", url="https://github.com/signalbot-org/signalbot", ) await context.send("Check this out:", link_preview=preview) # View-once message await context.send("This self-destructs after viewing.", view_once=True) ``` ``` -------------------------------- ### Send Message from SignalBot Instance Source: https://context7.com/signalbot-org/signalbot/llms.txt Sends a message directly via the SignalBot instance, useful for proactive messaging. Accepts various receiver types and can edit existing messages. ```python from signalbot import SignalBot, Config bot = SignalBot(Config( signal_service="127.0.0.1:8080", phone_number="+49123456789", )) async def notify(receiver: str, text: str) -> None: await bot.init_task # wait until bot is fully initialised await bot.send(receiver, text) # Edit a previously sent message async def update_status(receiver: str, old_ts: int) -> None: await bot.send(receiver, "Status: OK", edit_timestamp=old_ts) ``` -------------------------------- ### Context.reply() - Quoted Replies Source: https://context7.com/signalbot-org/signalbot/llms.txt Use Context.reply() to send a response that automatically quotes the original triggering message, threading the conversation. ```python from signalbot import Command, Context, triggered class QuoteReplyCommand(Command): @triggered("quote me") async def handle(self, context: Context) -> None: # Sends a reply that quotes context.message await context.reply("You said: " + context.message.text) ``` -------------------------------- ### Regex Pattern Trigger Decorator Source: https://context7.com/signalbot-org/signalbot/llms.txt Employ @regex_triggered to activate commands based on regular expression patterns in message text. It accepts raw strings or pre-compiled regex patterns. ```python import re from signalbot import Command, Context, regex_triggered class GmailDetector(Command): @regex_triggered(r"^[\w\.-]+@gmail\.com$") async def handle(self, context: Context) -> None: await context.send("Detected a Gmail address!") class MultiPatternCommand(Command): @regex_triggered( r"\d{4}-\d{2}-\d{2}", # ISO date re.compile(r"https?://\S+"), # URL ) async def handle(self, context: Context) -> None: await context.send("Found a date or URL in your message.") ``` -------------------------------- ### `@regex_triggered` decorator Source: https://context7.com/signalbot-org/signalbot/llms.txt Triggers a command when the message text matches any of the provided regex patterns. Accepts raw strings or pre-compiled `re.Pattern` objects. ```APIDOC ## `@regex_triggered` — regex-pattern trigger decorator Triggers `handle()` when the message text matches any of the provided regex patterns (via `re.search`). Accepts both raw strings and pre-compiled `re.Pattern` objects. ```python import re from signalbot import Command, Context, regex_triggered class GmailDetector(Command): @regex_triggered(r"^[\w\.-]+@gmail\.com$") async def handle(self, context: Context) -> None: await context.send("Detected a Gmail address!") class MultiPatternCommand(Command): @regex_triggered( r"\d{4}-\d{2}-\d{2}", # ISO date re.compile(r"https?://\S+"), # URL ) async def handle(self, context: Context) -> None: await context.send("Found a date or URL in your message.") ``` ``` -------------------------------- ### `@triggered` decorator Source: https://context7.com/signalbot-org/signalbot/llms.txt Triggers a command when the message text exactly matches one of the provided strings. Matching is case-insensitive by default. ```APIDOC ## `@triggered` — exact-text trigger decorator Triggers `handle()` only when the message text exactly matches one of the provided strings. Matching is case-insensitive by default; pass `case_sensitive=True` to override. ```python from signalbot import Command, Context, triggered class MultiWordCommand(Command): @triggered("hello", "hi", "hey") # any of these (case-insensitive) async def handle(self, context: Context) -> None: await context.send(f"Hello, {context.message.source}!") class ExactCaseCommand(Command): @triggered("START", case_sensitive=True) async def handle(self, context: Context) -> None: await context.send("Exact case match — started.") ``` ``` -------------------------------- ### bot.update_group() Source: https://context7.com/signalbot-org/signalbot/llms.txt Updates a Signal group's metadata, including its name, description, avatar, and expiration timer. This allows for dynamic management of group settings. ```APIDOC ## `bot.update_group()` — update group metadata Updates a Signal group's name, description, avatar, or expiration timer. ```python import base64 async def configure_group(bot, group_id: str) -> None: with open("avatar.png", "rb") as f: b64_avatar = base64.b64encode(f.read()).decode() await bot.update_group( group_id=group_id, name="Ops Team", description="Operations channel", expiration_in_seconds=604800, # 1 week base64_avatar=b64_avatar, ) ``` ``` -------------------------------- ### bot.send() Source: https://context7.com/signalbot-org/signalbot/llms.txt Sends a message directly via the SignalBot instance. This method is useful for proactive messaging, such as sending scheduled notifications or updating previous messages. ```APIDOC ## `bot.send()` — send a message from outside a command Sends a message directly via the `SignalBot` instance, useful for proactive (non-reactive) messaging such as scheduled jobs. Accepts phone numbers, UUIDs, Signal usernames, group IDs, and group names as `receiver`. ```python from signalbot import SignalBot, Config bot = SignalBot(Config( signal_service="127.0.0.1:8080", phone_number="+49123456789", )) async def notify(receiver: str, text: str) -> None: await bot.init_task # wait until bot is fully initialised await bot.send(receiver, text) # Edit a previously sent message async def update_status(receiver: str, old_ts: int) -> None: await bot.send(receiver, "Status: OK", edit_timestamp=old_ts) ``` ``` -------------------------------- ### Emoji Reaction Trigger Decorator Source: https://context7.com/signalbot-org/signalbot/llms.txt Use @reaction_triggered for commands that respond to emoji reactions on messages. Without arguments, it matches all reactions; specify emoji strings to filter. ```python from signalbot import Command, Context, reaction_triggered class AnyReactionCommand(Command): @reaction_triggered() async def handle(self, context: Context) -> None: reaction = context.message.reaction if reaction.is_remove: await context.send(f"Reaction {reaction.emoji} removed.") else: await context.send( f"{reaction.emoji} on message at {reaction.target_sent_timestamp}" ) class ThumbsUpCommand(Command): @reaction_triggered("👍", "❤️") # only these emoji async def handle(self, context: Context) -> None: await context.send("Thanks for the love!") ``` -------------------------------- ### `Context.edit()` Source: https://context7.com/signalbot-org/signalbot/llms.txt Edits a message previously sent by the bot, using the message's timestamp. The timestamp is obtained from the return value of `Context.send()`. ```APIDOC ## `Context.edit()` — edit a previously sent message Edits a message the bot already sent by supplying the original message's timestamp. The timestamp is returned by `Context.send()`. ```python import asyncio from signalbot import Command, Context, triggered class EditCommand(Command): @triggered("edit") async def handle(self, context: Context) -> None: timestamp = await context.send("This will be edited in 2 seconds…") await asyncio.sleep(2) await context.edit("Updated content!", timestamp) ``` ``` -------------------------------- ### `@reaction_triggered` decorator Source: https://context7.com/signalbot-org/signalbot/llms.txt Triggers a command when the incoming message is an emoji reaction. Can match all reactions or filter by specific emoji strings. ```APIDOC ## `@reaction_triggered` — emoji reaction trigger decorator Triggers `handle()` when the incoming message is an emoji reaction. Without arguments it matches all reactions; pass specific emoji strings to filter. ```python from signalbot import Command, Context, reaction_triggered class AnyReactionCommand(Command): @reaction_triggered() async def handle(self, context: Context) -> None: reaction = context.message.reaction if reaction.is_remove: await context.send(f"Reaction {reaction.emoji} removed.") else: await context.send( f"{reaction.emoji} on message at {reaction.target_sent_timestamp}" ) class ThumbsUpCommand(Command): @reaction_triggered("👍", "❤️") # only these emoji async def handle(self, context: Context) -> None: await context.send("Thanks for the love!") ``` ``` -------------------------------- ### Exact Text Trigger Decorator Source: https://context7.com/signalbot-org/signalbot/llms.txt Use @triggered for commands that respond to exact message text matches. Matching is case-insensitive by default; set `case_sensitive=True` for exact case matching. ```python from signalbot import Command, Context, triggered class MultiWordCommand(Command): @triggered("hello", "hi", "hey") # any of these (case-insensitive) async def handle(self, context: Context) -> None: await context.send(f"Hello, {context.message.source}!") class ExactCaseCommand(Command): @triggered("START", case_sensitive=True) async def handle(self, context: Context) -> None: await context.send("Exact case match — started.") ``` -------------------------------- ### Delete a Sent Message with Context.remote_delete() Source: https://context7.com/signalbot-org/signalbot/llms.txt Remotely deletes a previously sent message using its timestamp. Ensure the message timestamp is available before calling this function. ```python import asyncio from signalbot import Command, Context, triggered class DeleteCommand(Command): @triggered("delete") async def handle(self, context: Context) -> None: timestamp = await context.send("Deleting this in 2 seconds…") await asyncio.sleep(2) await context.remote_delete(timestamp=timestamp) ``` -------------------------------- ### Context.edit() - Editing Sent Messages Source: https://context7.com/signalbot-org/signalbot/llms.txt Edit a message previously sent by the bot using its timestamp, which is returned by Context.send(). Requires an asyncio.sleep for demonstration. ```python import asyncio from signalbot import Command, Context, triggered class EditCommand(Command): @triggered("edit") async def handle(self, context: Context) -> None: timestamp = await context.send("This will be edited in 2 seconds…") await asyncio.sleep(2) await context.edit("Updated content!", timestamp) ``` -------------------------------- ### Context.remote_delete() Source: https://context7.com/signalbot-org/signalbot/llms.txt Remotely deletes a previously sent message using its timestamp. This is useful for cleaning up messages or correcting errors after a message has been sent. ```APIDOC ## `Context.remote_delete()` — delete a sent message Remotely deletes a previously sent message using its timestamp. ```python import asyncio from signalbot import Command, Context, triggered class DeleteCommand(Command): @triggered("delete") async def handle(self, context: Context) -> None: timestamp = await context.send("Deleting this in 2 seconds…") await asyncio.sleep(2) await context.remote_delete(timestamp=timestamp) ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.