@@ -2172,29 +2172,98 @@ async def get_pinned_messages(chat_id: Union[int, str], account: str = None) ->
21722172@with_account (readonly = False )
21732173@validate_id ("chat_id" )
21742174async def create_poll (
2175- chat_id : int ,
2175+ chat_id : Union [ int , str ] ,
21762176 question : str ,
2177- options : list ,
2177+ options : Union [ List [ str ], List [ Dict [ str , Any ]], str ] ,
21782178 multiple_choice : bool = False ,
21792179 quiz_mode : bool = False ,
21802180 public_votes : bool = True ,
2181- close_date : str = None ,
2182- account : str = None ,
2181+ close_date : Optional [ str ] = None ,
2182+ account : Optional [ str ] = None ,
21832183) -> str :
21842184 """
21852185 Create a poll in a chat using Telegram's native poll feature.
21862186
21872187 Args:
2188- chat_id: The ID of the chat to send the poll to
2189- question: The poll question
2190- options: List of answer options (2-10 options)
2191- multiple_choice: Whether users can select multiple answers
2192- quiz_mode: Whether this is a quiz (has correct answer)
2193- public_votes: Whether votes are public
2194- close_date: Optional close date in ISO format (YYYY-MM-DD HH:MM:SS)
2188+ chat_id: The ID or username of the chat to send the poll to.
2189+ question: The poll question.
2190+ options: List of answer options (2-10 options). Can be a list of strings
2191+ or option objects, or a JSON string / comma-separated string.
2192+ multiple_choice: Whether users can select multiple answers.
2193+ quiz_mode: Whether this is a quiz (has correct answer).
2194+ public_votes: Whether votes are public.
2195+ close_date: Optional close date in ISO format (YYYY-MM-DD HH:MM:SS).
2196+ account: Account name to use (optional).
21952197 """
21962198 try :
2199+ # Validate question
2200+ if not question or not str (question ).strip ():
2201+ return "Error: Poll question cannot be empty."
2202+ question_text = str (question ).strip ()
2203+ if len (question_text ) > 300 :
2204+ return "Error: Poll question cannot exceed 300 characters."
2205+
2206+ # Parse and normalize options
2207+ if isinstance (options , str ):
2208+ options_str = options .strip ()
2209+ if options_str .startswith ("[" ) and options_str .endswith ("]" ):
2210+ try :
2211+ parsed = json .loads (options_str )
2212+ if isinstance (parsed , list ):
2213+ options = parsed
2214+ except Exception :
2215+ pass
2216+ if isinstance (options , str ):
2217+ sep = "\n " if "\n " in options_str else ","
2218+ options = [opt .strip () for opt in options_str .split (sep ) if opt .strip ()]
2219+
2220+ if not isinstance (options , (list , tuple )):
2221+ return "Error: Poll options must be a list of strings."
2222+
2223+ raw_options = options
2224+ normalized_options : List [str ] = []
2225+ for opt in raw_options :
2226+ if isinstance (opt , dict ):
2227+ # Try common keys used by LLMs: "option", "text", "value", "title", "label"
2228+ val = None
2229+ for key in ("option" , "text" , "value" , "title" , "label" ):
2230+ if key in opt and opt [key ] is not None :
2231+ val = str (opt [key ]).strip ()
2232+ break
2233+ if val is None :
2234+ # Pick the first non-empty value in the dict
2235+ for v in opt .values ():
2236+ if v is not None and str (v ).strip ():
2237+ val = str (v ).strip ()
2238+ break
2239+ opt_str = val if val is not None else ""
2240+ else :
2241+ opt_str = str (opt ).strip ()
2242+
2243+ if not opt_str :
2244+ return "Error: Poll options cannot be empty."
2245+ if len (opt_str ) > 100 :
2246+ return "Error: Each poll option cannot exceed 100 characters."
2247+ normalized_options .append (opt_str )
2248+
2249+ if len (normalized_options ) < 2 :
2250+ return "Error: Poll must have at least 2 options."
2251+ if len (normalized_options ) > 10 :
2252+ return "Error: Poll can have at most 10 options."
2253+
2254+ if len (set (normalized_options )) != len (normalized_options ):
2255+ return "Error: Poll options must be unique."
2256+
2257+ # Parse close date if provided
2258+ close_date_obj = None
2259+ if close_date :
2260+ try :
2261+ close_date_obj = datetime .fromisoformat (close_date .replace ("Z" , "+00:00" ))
2262+ except ValueError :
2263+ return "Invalid close_date format. Use YYYY-MM-DD HH:MM:SS format."
2264+
21972265 cl = get_client (account )
2266+ await ensure_connected (cl )
21982267 entity = await resolve_entity (chat_id , cl )
21992268
22002269 if is_chat_allowlist_enabled () and not is_chat_allowed (chat_id , entity ):
@@ -2207,30 +2276,16 @@ async def create_poll(
22072276 chat_id = chat_id ,
22082277 )
22092278
2210- # Validate options
2211- if len (options ) < 2 :
2212- return "Error: Poll must have at least 2 options."
2213- if len (options ) > 10 :
2214- return "Error: Poll can have at most 10 options."
2215-
2216- # Parse close date if provided
2217- close_date_obj = None
2218- if close_date :
2219- try :
2220- close_date_obj = datetime .fromisoformat (close_date .replace ("Z" , "+00:00" ))
2221- except ValueError :
2222- return f"Invalid close_date format. Use YYYY-MM-DD HH:MM:SS format."
2223-
22242279 # Create the poll using InputMediaPoll with SendMediaRequest
22252280 from telethon .tl .types import InputMediaPoll , Poll , PollAnswer , TextWithEntities
22262281 import random
22272282
22282283 poll = Poll (
22292284 id = random .randint (0 , 2 ** 63 - 1 ),
2230- question = TextWithEntities (text = question , entities = []),
2285+ question = TextWithEntities (text = question_text , entities = []),
22312286 answers = [
22322287 PollAnswer (text = TextWithEntities (text = option , entities = []), option = bytes ([i ]))
2233- for i , option in enumerate (options )
2288+ for i , option in enumerate (normalized_options )
22342289 ],
22352290 # Telethon 1.44 made `hash` a required argument on Poll. It caches
22362291 # server-side results, so a poll being created sends 0.
0 commit comments