目录结构

核心代码

完整代码

#!/usr/bin/python3

import anyio
import asyncari
import logging
import aioudp
import os
import vosk
import array

ast_host = os.getenv("AST_HOST", '127.0.0.1')
ast_port = int(os.getenv("AST_ARI_PORT", 8088))
ast_url = os.getenv("AST_URL", 'http://%s:%d/'%(ast_host,ast_port))
ast_username = os.getenv("AST_USER", 'asterisk')
ast_password = os.getenv("AST_PASS", 'asterisk')
ast_app = os.getenv("AST_APP", 'hello-world')


model = vosk.Model(lang='en-us')
channels = {}

class Channel:

    async def rtp_handler(self, connection):
        async for message in connection:
            data = array.array('h', message[12:])
            data.byteswap()
            if self.rec.AcceptWaveform(data.tobytes()):
                res = self.rec.Result()
            else:
                res = self.rec.PartialResult()
            print(res)

    async def init(self, client, channel):
        self.port = 45000 + len(channels)
        self.rec = vosk.KaldiRecognizer(model, 16000)
        self.udp = aioudp.serve("127.0.0.1", self.port, self.rtp_handler)
        await self.udp.__aenter__()

        bridge = await client.bridges.create(type='mixing')
        media_id = client.generate_id()
        await client.channels.externalMedia(channelId=media_id, app=client._app, external_host='127.0.0.1:' + str(self.port), format='slin16')
        await bridge.addChannel(channel=[media_id, channel.id])

async def statis_handler(objs, ev, client):
    channel = objs['channel']
    channel.answer()
    if 'UnicastRTP' in channel.name:
         return

    local_channel = Channel()
    await local_channel.init(client, channel)
    channels[channel.id] = local_channel

async def main():
     async with asyncari.connect(ast_url, ast_app, ast_username,ast_password) as client:
         async with client.on_channel_event('StasisStart') as listener:
             async for objs, event in listener:
                  await statis_handler(objs, event, client)

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    anyio.run(main)

调用方法

This is an example of channel transcription through ARI/externalMedia

To configure statis make sure modules are loaded:

```
load = res_rtp_multicast.so
load = res_ari.so ; Asterisk RESTful Interface
load = res_ari_applications
load = res_ari_asterisk.so
load = res_ari_bridges
load = res_ari_channels.so
load = res_ari_device_states.so ; RESTful API module - Device state resources
load = res_ari_endpoints.so
load = res_ari_events.so
load = res_ari_model.so ; ARI Model validators
load = res_ari_playbacks.so
load = res_ari_recordings.so
load = res_ari_sounds
load = res_stasis.so ; Stasis application support
load = app_stasis.so ; Stasis dialplan application
load = res_stasis_answer.so
load = res_stasis_device_state.so ; Stasis application device state support
load = res_stasis_playback.so ; Stasis application playback support
load = res_stasis_recording.so ; Stasis application recording support
load = res_stasis_snoop.so ; Stasis application snoop support
```

then check ari.conf:

```
[general]
enabled = yes          
pretty = yes           
allowed_origins = *

[asterisk]
type = user
read_only = no
password = asterisk
```

Then the dialplan

```
exten => 4,1,Answer()
same =  n,Stasis(hello-world)
same =  n,Hangup()
```

Install required python modules


```
pip3 install aioudp asyncari vosk
```

And start the statis app:

```
python3 vosk_ari.py
```

Dial the extension, you should see the transcript on console

示意图

实现原理

一、整体功能(仙盟视角总览)

这段内容本质是搭建一套「仙盟语音传令系统」:

  • Asterisk(星号服务器)是仙盟总坛,负责接收各路弟子(电话)的语音传令;
  • ARI(Asterisk RESTful 接口)是仙盟传讯令符,让外部弟子(Python 程序)能和总坛互通消息;
  • Stasis 是总坛议事堂,专门处理需要特殊处理的传令(比如语音转写);
  • Vosk 是仙盟译经师,把弟子的口语传令(语音)转成文字(转写);
  • 整个流程:弟子拨打总坛分机号→总坛接起→把传令转到议事堂→译经师实时翻译→把文字版传令显示在总坛控制台。

二、逐段拆解(仙盟场景对应解释)

1. 加载 Asterisk 模块(给总坛配备法器)

plaintext

load = res_rtp_multicast.so
load = res_ari.so ; Asterisk RESTful Interface
...
load = res_stasis_snoop.so ; Stasis application snoop support
  • 比喻:这些load = xxx.so是给仙盟总坛配备核心法器 ——
    • res_rtp_multicast.so:负责语音信号传输的「传音法器」;
    • res_ari.so及相关:激活「传讯令符(ARI)」,让外部程序能和总坛对话;
    • res_stasis.so/app_stasis.so:搭建「议事堂(Stasis)」,专门处理特殊传令;
    • 其他res_stasis_xxx.so:给议事堂配辅助人员(处理接听、录音、监听等)。
  • 作用:只有加载这些模块,Asterisk 才能支持 ARI 接口和 Stasis 应用,是后续功能的基础。
2. 配置 ari.conf(给传讯令符设使用规则)

plaintext

[general]
enabled = yes          ; 启用传讯令符
pretty = yes           ; 令符返回的消息格式工整(方便人看)
allowed_origins = *    ; 允许所有外部弟子使用令符(无限制)

[asterisk]
type = user            ; 定义使用令符的弟子身份
read_only = no         ; 弟子不仅能看消息,还能发指令
password = asterisk    ; 使用令符的密码(简单密码仅作示例)
  • 比喻:给「传讯令符(ARI)」定规矩 —— 允许使用、谁能用、能做什么。
  • 作用:开启 ARI 接口,设置访问权限和认证信息,让 Python 程序能通过 ARI 控制 Asterisk。
3. 配置拨号计划(给总坛设接令规则)

plaintext

exten => 4,1,Answer()  ; 弟子拨打分机号4时,总坛先接起传令
same =  n,Stasis(hello-world)  ; 把这个传令转到「hello-world议事堂」处理
same =  n,Hangup()     ; 处理完后挂断传令
  • 比喻:仙盟总坛的接令规则 —— 只要有人打分机 4,先接电话,再把这个通话交给名为hello-world的议事堂处理,最后挂电话。
  • 作用:定义电话呼入后的处理逻辑,核心是把通话交给 Stasis 应用,让外部 Python 程序能接管通话的语音处理。
4. 安装 Python 模块(给译经师配工具)

plaintext

pip3 install aioudp asyncari vosk
  • 比喻:给「译经师(Vosk)」配备工作工具 ——
    • vosk:译经师的核心本领(语音转文字);
    • asyncari:让译经师能看懂「传讯令符(ARI)」的消息格式;
    • aioudp:译经师接收语音数据的「收音法器」。
  • 作用:安装 Python 依赖库,让后续的vosk_ari.py程序能通过 ARI 连接 Asterisk,并调用 Vosk 实现语音转写。
5. 启动 Stasis 应用(让译经师到议事堂上班)

plaintext

python3 vosk_ari.py
  • 比喻:让带着工具的译经师(vosk_ari.py程序)到「hello-world 议事堂」上班,等待处理传令。
  • 作用:运行 Python 程序,该程序会通过 ARI 连接 Asterisk,监听 Stasis 应用的事件,当有通话进入 Stasis 时,实时获取语音流,用 Vosk 转写并输出到控制台。
6. 最终效果(完整流程)

拨号分机 4 → Asterisk 接起通话 → 把通话交给 Stasis → Python 程序通过 ARI 获取通话语音 → Vosk 转写语音为文字 → 控制台显示转写结果。

东方仙盟:拥抱知识开源,共筑数字新生态


在全球化与数字化浪潮中,东方仙盟始终秉持开放协作、知识共享的理念,积极拥抱开源技术与开放标准。我们相信,唯有打破技术壁垒、汇聚全球智慧,才能真正推动行业的可持续发展。

开源赋能中小商户:通过将前端异常检测、跨系统数据互联等核心能力开源化,东方仙盟为全球中小商户提供了低成本、高可靠的技术解决方案,让更多商家能够平等享受数字转型的红利。
共建行业标准:我们积极参与国际技术社区,与全球开发者、合作伙伴共同制定开放协议  与技术规范,推动跨境零售、文旅、餐饮等多业态的系统互联互通,构建更加公平、高效的数字生态。
知识普惠,共促发展:通过开源社区 、技术文档与培训体系,东方仙盟致力于将前沿技术转化为可落地的行业实践,赋能全球合作伙伴,共同培育创新人才,推动数字经济 的普惠式增长


阿雪技术观

在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目 维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基 生命,为科技进步添砖加瓦。

Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're tossing out your code snippets    , hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open - source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome    place where we all come together. We're gonna team up and explore the whole silicon - based life thing, and in the process, we'll be fueling the growth of technology 

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐