# Extensions – JobQueue(翻譯)
###### tags: `telegram` `bot`
>[name=shaoe.chen]
:::danger
[官方文件](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue)
:::
[TOC]
## Introduction
擴展類別`telegram.ext.JobQueue`允許你以設置的間隔延遲,甚至週期性的執行任務。除此之外,你還可以用它向訂閱戶發送定期更新。
## Usage
類別`JobQueue`與其它類別`telegram.ext`緊密結合在一起。就像是`Updater`與`Dispatcher`,它在單獨的執行緒中非同步的執行。
使用`JobQueue`並不需要做太多設置。當你實例化`Updater`的必的,它將為你建立一個`JobQueue`:
```python
import telegram.ext
from telegram.ext import Updater
u = Updater('TOKEN', use_context=True)
j = u.job_queue
```
這個job_queue會同時連結到dispatcher,這文章後續會討論到。只要知道,除非你有很好的理由,否則不要自己實例化`JobQueue`。
工作隊列中的任務是由類別`Job`封裝。它有一個callback function做為參數,能夠在時間到來的時候執行。這個callback function始終帶著一個參數:`context`,一個`telegram.ext.CallbackContext`。就像是`Dispatcher`使用的程序處理callback一樣,透過這個物件,你可以訪問`context.bot`,也就是`Updater`的`telegram.Bot`的實例;而且在這特殊的情況下,你還可以訪問`context.job`,這是觸發callback的任務的`Job`實例(更多在說面說明)。
你可以使用下面三種方法來建立具有不同頻率與時間的工作:`job_queue.run_once`、`job_queue.run_repeating`與`job_queue.run_daily`。(正如之前所說,你通常不需要直接實例化類別`Job`)
## Tutorial
透過定義一個callback function並將它加入工作隊列來建立第一個工作到隊列。在這個教程中,你可以將`'@examplechannel'`替換為你的機器人是管理員的頻道,或是你的用戶id(使用[@userinfobot](https://telegram.me/userinfobot)來尋找你的用戶id):
```python
def callback_minute(context: telegram.ext.CallbackContext):
context.bot.send_message(chat_id='@examplechannel',
text='One message every minute')
job_minute = j.run_repeating(callback_minute, interval=60, first=0)
```
(如果你使用的是Python 2,請忽略類型的註解方式)
函數`callback_minute`將會每`60.0`秒執行一次,第一次就是現在(因為`first=0`)。參數`interval`與`first`如果是`int`或`float`的話,那就是以秒為單位。它們也可以是`datetime`物件。詳細資訊請參考[文件](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.jobqueue.html)說明。這些函數回傳的值是被建立的`Job`物件。如果沒有必要,你並不需要保存`run_repeating`(新實例化的`Job`)的結果;我們會在這個教程的後面使用它。你也可以增加一個執行一次而且會延遲的工作:
```python
def callback_30(context: telegram.ext.CallbackContext):
context.bot.send_message(chat_id='@examplechannel',
text='A single message with 30s delay')
j.run_once(callback_30, 30)
```
三十秒之後你應該會接收到來自`callback_30`的訊息。
如果你厭倦每分鐘都收到訊息,你可以暫時關閉工作,甚至從隊列直接移除它。
```python
job_minute.enabled = False # Temporarily disable this job
job_minute.schedule_removal() # Remove this job completely
```
**注意:** `schedule_removal`並不會立即從隊列中移除工作。而是將它標記為刪除,並在當前的間隔結束之後立即移除(標記刪除之後它將不會再執行)。
工作還可以改變自己的行為,因為它作為第二個參數傳給callback function:
```python
def callback_increasing(context: telegram.ext.CallbackContext):
job = context.job
context.bot.send_message(chat_id='@examplechannel',
text='Sending messages with increasing delay up to 10s, then stops.')
job.interval += 1.0
if job.interval > 10.0:
job.schedule_removal()
j.run_repeating(callback_increasing, 1)
```
這個工作會在一秒之後發送第一則訊息,再過兩秒發送第二則,再過三秒發送第三則,以此類推。在十則訊息之後它將自我終結。
你也許增加一個工作來回應某些使用者的輸入,有一個很方便的方法可以這麼做。如果你需要,所有的`Handler`類都可以傳遞工作隊列到它們的callback function。要做到這一點,你只需要在實例化`Handler`的時候設置`pass_job_queue=True`。你可以在這邊使用的另一個功能是`Job`的`context`關鍵參數。啟動工作的時候,你可以傳遞任何的物件做為下上文參數,在後續的階段只要工作存在就可以檢索它。讓我們看一下程式碼:
```python
from telegram.ext import CommandHandler
def callback_alarm(context: telegram.ext.CallbackContext):
context.bot.send_message(chat_id=context.job.context, text='BEEP')
def callback_timer(update: telegram.Update, context: telegram.ext.CallbackContext):
context.bot.send_message(chat_id=update.message.chat_id,
text='Setting a timer for 1 minute!')
context.job_queue.run_once(callback_alarm, 60, context=update.message.chat_id)
timer_handler = CommandHandler('timer', callback_timer)
u.dispatcher.add_handler(timer_handler)
```
透過放置`chat_id`在`Job`物件中,callback function就會知道它應該把訊息發送到那。
所有的好事都結束了,因此當你停止`Updater`的時候,相關的工作隊列也會同時停止:
```python
u.stop()
```
當然,你也可以自己停止隊列:
```python
j.stop()
```