Skip to content

Commit 434caef

Browse files
authored
Merge pull request #282 from l3vels/feature/telegram-bot
Feature/telegram bot
2 parents 40eed1a + 85cbfcd commit 434caef

File tree

39 files changed

+21978
-2251
lines changed

39 files changed

+21978
-2251
lines changed
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from typing import List
2+
3+
from fastapi import APIRouter
4+
5+
from integrations.get_integrations import get_all_integration_providers
6+
from typings.integrations import IntegrationOutput
7+
8+
router = APIRouter()
9+
10+
11+
@router.get("", response_model=List[IntegrationOutput])
12+
def get_integrations() -> List[IntegrationOutput]:
13+
"""
14+
Get all integrations
15+
16+
"""
17+
18+
return get_all_integration_providers()

apps/server/integrations/__init__.py

Whitespace-only changes.

apps/server/integrations/base.py

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from abc import abstractmethod
2+
from enum import Enum
3+
from typing import Dict, List, Optional
4+
5+
from pydantic import BaseModel, Field, validator
6+
7+
from models.config import ConfigModel
8+
from typings.agent import AgentOutput, ConfigsOutput
9+
from typings.config import ConfigQueryParams
10+
11+
12+
class IntegrationEnvKeyType(Enum):
13+
STRING = "string"
14+
FILE = "file"
15+
INT = "int"
16+
17+
def __str__(self):
18+
return self.value
19+
20+
21+
class IntegrationEnvKey(BaseModel):
22+
label: str = Field()
23+
key: str = Field()
24+
key_type: IntegrationEnvKeyType = Field(default=IntegrationEnvKeyType.STRING)
25+
is_required: bool = Field(default=False)
26+
is_secret: bool = Field(default=False)
27+
default_value: str = Field(default=None)
28+
29+
@validator("is_secret", "is_required", pre=True, always=True)
30+
def check_bool(cls, v):
31+
"""Check if the value is a boolean."""
32+
if v is None:
33+
return False
34+
elif isinstance(v, bool):
35+
return v
36+
else:
37+
raise ValueError("Value should be a boolean")
38+
39+
@validator("key_type", pre=True, always=True)
40+
def check_key_type(cls, v):
41+
"""Check if the value is a boolean."""
42+
if v is None:
43+
return IntegrationEnvKeyType.STRING
44+
elif isinstance(v, IntegrationEnvKeyType):
45+
return v
46+
else:
47+
raise ValueError("key_type should be string/file/integer")
48+
49+
50+
class BaseIntegrationTools:
51+
id: str
52+
configs: Dict[str, str] = {}
53+
settings: Optional[ConfigsOutput] = None
54+
voice_slug: Optional[str] = None
55+
agent: Optional[AgentOutput] = None
56+
57+
def get_env_key(self, key: str):
58+
return self.configs.get(key)
59+
60+
61+
class BaseIntegration(BaseModel):
62+
id: str
63+
name: str
64+
description: str
65+
slug: str
66+
is_active: bool = Field(default=True)
67+
68+
def get_tools_with_configs(
69+
self, db, account, settings, agent_with_configs
70+
) -> List[BaseIntegrationTools]:
71+
configs = ConfigModel.get_configs(
72+
db=db, query=ConfigQueryParams(toolkit_id=self.id), account=account
73+
)
74+
config_dict = {config.key: config.value for config in configs}
75+
tools = self.get_tools()
76+
77+
for tool in tools:
78+
tool.configs = config_dict
79+
tool.voice_slug = self.slug
80+
tool.settings = settings
81+
tool.account = account
82+
83+
return tools
84+
85+
@abstractmethod
86+
def get_env_keys(self) -> List[IntegrationEnvKey]:
87+
# Add file related config keys here
88+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from typing import List
2+
3+
from integrations.base import BaseIntegration
4+
from integrations.telegram.telegram_integration import TelegramIntegration
5+
from typings.integrations import IntegrationOutput
6+
7+
INTEGRATIONS: List[BaseIntegration] = [
8+
TelegramIntegration(),
9+
]
10+
11+
12+
COMING_SOON = [
13+
{
14+
"is_public": True,
15+
"is_active": False,
16+
"name": "WhatsApp",
17+
"description": "Connect L3AGI chat with WhatsApp for smooth communication between sessions.",
18+
},
19+
{
20+
"is_public": True,
21+
"is_active": False,
22+
"name": "Facebook Messenger",
23+
"description": "Connect L3AGI chat with Facebook's Messenger for smooth communication between sessions.",
24+
},
25+
]
26+
27+
28+
def get_all_integration_providers():
29+
"""Return a list of all integrations."""
30+
result = []
31+
32+
for integration in INTEGRATIONS:
33+
result.append(
34+
IntegrationOutput(
35+
id=integration.id,
36+
is_public=True,
37+
is_active=integration.is_active,
38+
name=integration.name,
39+
slug=integration.slug,
40+
description=integration.description,
41+
fields=[
42+
{
43+
"label": env_key.label,
44+
"key": env_key.key,
45+
"type": str(env_key.key_type),
46+
"is_required": env_key.is_required,
47+
"is_secret": env_key.is_secret,
48+
"default_value": str(env_key.default_value)
49+
if env_key.default_value is not None
50+
else None,
51+
}
52+
for env_key in integration.get_env_keys()
53+
],
54+
# tools=[
55+
# {
56+
# "tool_id": tool.tool_id,
57+
# "name": tool.name,
58+
# "description": tool.description,
59+
# }
60+
# for tool in voice.get_tools()
61+
# ],
62+
)
63+
)
64+
65+
for integration in COMING_SOON:
66+
result.append(
67+
IntegrationOutput(
68+
id=None, # Update this if COMING_SOON has an 'id' field
69+
is_public=integration["is_public"],
70+
is_active=integration["is_active"],
71+
name=integration["name"],
72+
description=integration["description"],
73+
fields=[], # Update this if COMING_SOON has a 'fields' field
74+
)
75+
)
76+
77+
return result

apps/server/integrations/telegram/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from abc import ABC
2+
from typing import List
3+
4+
from integrations.base import (BaseIntegration, IntegrationEnvKey,
5+
IntegrationEnvKeyType)
6+
7+
8+
class TelegramIntegration(BaseIntegration, ABC):
9+
name: str = "Telegram Bot"
10+
description: str = "Connect L3AGI chat with a Telegram bot for smooth communication between sessions."
11+
slug: str = "telegram"
12+
13+
id = "2c2849ab-c74e-485c-9603-b8a18280c7b1"
14+
15+
def get_env_keys(self) -> List[IntegrationEnvKey]:
16+
return [
17+
IntegrationEnvKey(
18+
label="Bot API Token Key",
19+
key="TELEGRAM_BOT_API_KEY",
20+
key_type=IntegrationEnvKeyType.STRING,
21+
is_required=True,
22+
is_secret=True,
23+
),
24+
IntegrationEnvKey(
25+
label="Bot Username",
26+
key="TELEGRAM_BOT_USERNAME",
27+
key_type=IntegrationEnvKeyType.STRING,
28+
is_required=False,
29+
is_secret=False,
30+
),
31+
]

apps/server/main.py

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from controllers.datasource import router as datasource_router
1919
from controllers.file import router as file_router
2020
from controllers.fine_tuning import router as fine_tuning_router
21+
from controllers.integrations import router as integrations_router
2122
from controllers.llm import router as llm_router
2223
from controllers.model import router as model_router
2324
from controllers.run import router as run_router
@@ -110,6 +111,7 @@ def jwt_exception_handler(request: Request, exc: AuthJWTException):
110111
app.include_router(model_router, prefix="/model")
111112
app.include_router(schedule_router, prefix="/schedule")
112113
app.include_router(api_key_router, prefix="/api-key")
114+
app.include_router(integrations_router, prefix="/integrations")
113115
app.include_router(fine_tuning_router, prefix="/fine-tuning")
114116

115117

apps/server/typings/agent.py

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class ConfigInput(BaseModel):
3333
suggestions: Optional[List[str]]
3434
greeting: Optional[str]
3535
text: Optional[str]
36+
integrations: Optional[List[dict]]
3637
source_flow: Optional[str]
3738
synthesizer: Optional[str]
3839
default_voice: Optional[str]
@@ -58,6 +59,7 @@ class ConfigsOutput(BaseModel):
5859
suggestions: Optional[List[str]]
5960
greeting: Optional[str]
6061
text: Optional[str]
62+
integrations: Optional[List[dict]]
6163
source_flow: Optional[str]
6264
synthesizer: Optional[str]
6365
default_voice: Optional[str]

apps/server/typings/integrations.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from typing import List, Optional
2+
3+
from pydantic import UUID4, BaseModel
4+
5+
6+
class IntegrationFieldOutput(BaseModel):
7+
label: str
8+
key: str
9+
type: str
10+
is_required: bool
11+
is_secret: bool
12+
default_value: Optional[str] = None
13+
14+
15+
class IntegrationOutput(BaseModel):
16+
id: Optional[UUID4]
17+
is_active: bool
18+
is_public: bool
19+
name: str
20+
description: Optional[str]
21+
slug: Optional[str]
22+
fields: Optional[List[IntegrationFieldOutput]]

apps/server/utils/configs/default.py

+1
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@
7272
},
7373
"contact": False,
7474
"group": False,
75+
"external-links": True,
7576
},
7677
}

apps/server/utils/configs/levanion_configs.py

+1
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,6 @@
7373
"schedule": True,
7474
"contact": True,
7575
"group": True,
76+
"external-links": True,
7677
},
7778
}

0 commit comments

Comments
 (0)