This commit is contained in:
yumoqing 2026-03-09 13:50:08 +08:00
parent 0e5bfc492d
commit 10af1ca2ad
11 changed files with 222 additions and 0 deletions

16
config/tenants.json Normal file
View File

@ -0,0 +1,16 @@
[
{
"org_id":"org_demo",
"platform":"feishu",
"app_id":"cli_xxx",
"app_secret":"xxx"
},
{
"org_id":"org_demo2",
"platform":"wecom",
"corp_id":"wwxxx",
"agent_id":"1000002",
"secret":"xxx"
}
]

View File

@ -0,0 +1,8 @@
class BaseAdapter:
def __init__(self, tenant):
self.tenant = tenant
async def send_text(self, user, text):
raise NotImplementedError()

View File

@ -0,0 +1,19 @@
import aiohttp
from im_platform.adapters.base import BaseAdapter
class FeishuAdapter(BaseAdapter):
async def send_text(self, user, text):
url = "https://open.feishu.cn/open-apis/im/v1/messages"
payload = {
"receive_id": user,
"msg_type": "text",
"content": {"text": text}
}
async with aiohttp.ClientSession() as s:
async with s.post(url, json=payload) as r:
return await r.text()

View File

@ -0,0 +1,20 @@
import aiohttp
from im_platform.adapters.base import BaseAdapter
class WeComAdapter(BaseAdapter):
async def send_text(self, user, text):
url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"
payload = {
"touser": user,
"msgtype": "text",
"agentid": self.tenant["agent_id"],
"text": {"content": text}
}
async with aiohttp.ClientSession() as s:
async with s.post(url, json=payload) as r:
return await r.text()

View File

@ -0,0 +1,17 @@
from aiohttp import web
async def feishu_webhook(request):
data = await request.json()
print("feishu event", data)
return web.json_response({"ok": True})
async def wecom_webhook(request):
data = await request.json()
print("wecom event", data)
return web.json_response({"ok": True})

View File

@ -0,0 +1,24 @@
import json
from pathlib import Path
TENANT_CACHE = {}
async def load_tenants():
global TENANT_CACHE
p = Path(__file__).resolve().parent.parent / "config" / "tenants.json"
if p.exists():
TENANT_CACHE = json.loads(p.read_text())
return TENANT_CACHE
def get_tenant_by_org(org_id):
for t in TENANT_CACHE:
if t["org_id"] == org_id:
return t
return None
def get_tenant_by_platform(platform, key):
for t in TENANT_CACHE:
if t["platform"] == platform and (t.get("app_id")==key or t.get("corp_id")==key):
return t
return None

View File

@ -0,0 +1,17 @@
import asyncio
import time
CACHE = {}
async def get_token(key):
v = CACHE.get(key)
if not v:
return None
token, expire = v
if expire < time.time():
return None
return token
async def set_token(key, token, ttl):
CACHE[key] = (token, time.time() + ttl - 60)

View File

@ -0,0 +1,63 @@
{
"summary": [
{
"name": "im_tenant",
"title": "IM\u79df\u6237",
"primary": "id",
"catelog": "relation"
}
],
"fields": [
{
"name": "id",
"title": "ID",
"type": "str",
"length": 32
},
{
"name": "org_id",
"title": "\u7ec4\u7ec7ID",
"type": "str",
"length": 32
},
{
"name": "platform",
"title": "\u5e73\u53f0",
"type": "str",
"length": 16
},
{
"name": "app_id",
"title": "APPID",
"type": "str",
"length": 64
},
{
"name": "app_secret",
"title": "APPSECRET",
"type": "str",
"length": 128
},
{
"name": "corp_id",
"title": "\u4f01\u4e1aID",
"type": "str",
"length": 64
},
{
"name": "agent_id",
"title": "AgentID",
"type": "str",
"length": 32
}
],
"indexes": [
{
"name": "idx_org",
"idxtype": "index",
"idxfields": [
"org_id"
]
}
]
}

16
im_platform/server.py Normal file
View File

@ -0,0 +1,16 @@
from aiohttp import web
from im_platform.api.webhooks import feishu_webhook, wecom_webhook
async def init_app():
app = web.Application()
app.router.add_post("/webhook/feishu", feishu_webhook)
app.router.add_post("/webhook/wecom", wecom_webhook)
return app
if __name__ == "__main__":
import asyncio
app = asyncio.run(init_app())
web.run_app(app, port=8080)

View File

@ -0,0 +1,13 @@
import asyncio
QUEUE = asyncio.Queue()
async def push_event(event):
await QUEUE.put(event)
async def worker():
while True:
event = await QUEUE.get()
print("process event", event)

9
pyproject.toml Normal file
View File

@ -0,0 +1,9 @@
[project]
name = "im_platform"
version = "1.0.0"
dependencies = [
"ahserver",
"cryptography",
"redis"
]