"""Create a native Discord poll in a channel."""
from __future__ import annotations
import logging
from datetime import timedelta
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from tool_context import ToolContext
logger = logging.getLogger(__name__)
TOOL_NAME = "create_poll"
TOOL_DESCRIPTION = (
"Create a native Discord poll in a channel. Polls appear as "
"interactive vote widgets. Great for driving channel engagement "
"and gathering community opinions."
)
TOOL_PARAMETERS = {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The poll question (max 300 characters).",
},
"answers": {
"type": "array",
"items": {"type": "string"},
"description": (
"List of answer options (2-10 items, each max 55 characters)."
),
},
"channel_id": {
"type": "string",
"description": (
"Channel to post the poll in. If omitted, uses the "
"current channel."
),
},
"duration_hours": {
"type": "integer",
"description": (
"How long the poll stays open, in hours "
"(1-168, default 24)."
),
},
"allow_multiselect": {
"type": "boolean",
"description": (
"Whether users can vote for multiple options "
"(default false)."
),
},
},
"required": ["question", "answers"],
}
[docs]
async def run(
question: str,
answers: list[str],
channel_id: str | None = None,
duration_hours: int = 24,
allow_multiselect: bool = False,
ctx: ToolContext | None = None,
) -> str:
"""Create and send a Discord poll.
Args:
question: The poll question text.
answers: List of answer option strings.
channel_id: Target channel ID. Falls back to context channel.
duration_hours: Poll duration in hours (1-168).
allow_multiselect: Allow voting for multiple options.
ctx: Tool execution context.
Returns:
Result string.
"""
import discord
from tools._discord_helpers import require_discord_client
client = require_discord_client(ctx)
if isinstance(client, str):
return client
# --- Validate inputs ---
if not question or len(question) > 300:
return "Error: Question must be 1-300 characters."
if not answers or len(answers) < 2:
return "Error: At least 2 answer options are required."
if len(answers) > 10:
return "Error: Maximum 10 answer options allowed."
for i, a in enumerate(answers):
if not a or len(a) > 55:
return f"Error: Answer {i + 1} must be 1-55 characters."
duration_hours = max(1, min(168, duration_hours))
# --- Resolve channel ---
channel = None
if channel_id:
try:
channel = client.get_channel(int(channel_id))
except ValueError:
return f"Error: Invalid channel ID: '{channel_id}'."
if channel is None:
try:
channel = await client.fetch_channel(int(channel_id))
except Exception:
return f"Error: Channel '{channel_id}' not found."
elif ctx and ctx.channel_id:
try:
channel = client.get_channel(int(ctx.channel_id))
except (ValueError, TypeError):
pass
if channel is None:
return "Error: Could not resolve a channel. Please provide a channel_id."
if not hasattr(channel, "send"):
return f"Error: Channel '{channel_id or ctx.channel_id}' is not a text channel."
# --- Build and send the poll ---
try:
poll = discord.Poll(
question=question,
duration=timedelta(hours=duration_hours),
multiple=allow_multiselect,
)
for answer_text in answers:
poll.add_answer(text=answer_text)
await channel.send(poll=poll)
ch_name = getattr(channel, "name", str(channel.id))
return (
f"Poll created in #{ch_name}!\n"
f"Question: {question}\n"
f"Options: {', '.join(answers)}\n"
f"Duration: {duration_hours}h | "
f"Multi-select: {'yes' if allow_multiselect else 'no'}"
)
except discord.Forbidden:
return "Error: No permission to send polls in this channel."
except AttributeError:
return (
"Error: discord.Poll is not available. "
"This requires discord.py 2.4 or newer."
)
except Exception as exc:
logger.exception("Failed to create poll")
return f"Error creating poll: {exc}"