Skip to content

Commit

Permalink
Changed Project Structure, added Models and options for commands (#16)
Browse files Browse the repository at this point in the history
* fixed readme

* add text models and support options for text

* use middleware to replace utils

* add audio models and options

* temperorily removed welcome

* updated python version

* removed env from workflow

* add config in workflow
  • Loading branch information
ravachol-yang authored Mar 28, 2024
1 parent 4fc6dce commit e005270
Show file tree
Hide file tree
Showing 29 changed files with 514 additions and 289 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ permissions:
jobs:
tests:
runs-on: ubuntu-latest
env:
BOT_TOKEN: "example"
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: "3.10"
python-version: "3.11"
- name: Set up system environment
run: |
sudo apt-get update -y
Expand All @@ -28,6 +26,9 @@ jobs:
run: |
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
- name: Setup configurations
run: |
cp .env.toml env.toml
- name: Test with pytest
run: |
python3 -m pytest
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Randomology
<a href="https://github.com/ravachol-yang/randomology/actions"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/ravachol-yang/randomology/pytest.yml?style=for-the-badge&logo=github&label=tests"></a> <a href="https://t.me/randomology_bot"><img alt="Static Badge" src="https://img.shields.io/badge/telegram-grey?style=for-the-badge&logo=telegram"></a>

[![Github Actions Status](https://img.shields.io/github/actions/workflow/status/ravachol-yang/randomology/pytest.yml?style=for-the-badge&logo=github&label=tests)](https://github.com/ravachol-yang/randomology/actions)
[![Telegram](https://img.shields.io/badge/telegram-26a4e2?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/randomology_bot)

A telegram bot to generate random stuff, I built this to chat with my friend randomly.
Using [pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI) for talking with telegram server.
Expand Down Expand Up @@ -67,7 +67,7 @@ copy and change the config file to configure Nginx:
``` shell
cp nginx.conf /etc/nginx/sites-available/example.com
# don't forget to change it !!
ln /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled
```
restart `nginx.service`
### Running
Expand All @@ -88,6 +88,6 @@ now you can run it !
## LICENSE
*(&%&^*&(*&%*^(&)))

<a href="https://github.com/ravachol-yang/randomology/blob/master/LICENSE"><img alt="GitHub License" src="https://img.shields.io/github/license/ravachol-yang/randomology?style=for-the-badge"></a>
[![License](https://img.shields.io/github/license/ravachol-yang/randomology?style=for-the-badge)](https://github.com/ravachol-yang/randomology/blob/master/LICENSE)


36 changes: 12 additions & 24 deletions app/handlers/audio_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,23 @@
from telebot import TeleBot
from telebot.types import Message

from app.services.audio_service import generate_random_noise
from app.services.audio_service import generate_random_sine
from app.services.audio_service import generate_random_mix
from app.services.audio_service import generate_random_voice
from app.models.audio import Audio
from app.models.voice import Voice

# invoke with "/noise" command
def get_random_noise(message: Message, bot: TeleBot):
# invoke with "/audio" command
def get_random_audio(message: Message, bot: TeleBot, data: dict):
# pretend to be uploading audio
bot.send_chat_action(message.chat.id, "upload_voice")
noise = generate_random_noise()
bot.send_audio(message.chat.id, noise)
options = data['options']

# invoke with "/sine" command
def get_random_sine(message: Message, bot: TeleBot):
# uploading audio...
bot.send_chat_action(message.chat.id, "upload_voice")
sine = generate_random_sine()
bot.send_audio(message.chat.id, sine)

# invoke with "/mix" command
def get_random_mix(message: Message, bot: TeleBot):
# uploading audio...
bot.send_chat_action(message.chat.id, "upload_voice")
mix = generate_random_mix()
bot.send_audio(message.chat.id, mix)
audio = Audio(bot, message)
audio.generate(options).to_mpeg().send()

# invoke with "/voice" command
def get_random_voice(message: Message, bot: TeleBot):
def get_random_voice(message: Message, bot: TeleBot, data: dict):
# pretend to be sending voice
bot.send_chat_action(message.chat.id, "record_voice")
voice = generate_random_voice()
bot.send_voice(message.chat.id, voice)
options = data['options']

voice = Voice(bot, message)
voice.generate(options).to_voice().send()
12 changes: 9 additions & 3 deletions app/handlers/info_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
from telebot import TeleBot
from telebot.types import Message

from app.services.info_service import generate_info
from app.models.text import Text
from configs import templates

def get_info(message: Message, bot: TeleBot):
bot.send_chat_action(message.chat.id, "typing")

name = message.from_user.first_name
id = message.from_user.id
info = generate_info(name, id)
bot.send_message(message.chat.id, info, parse_mode="MarkdownV2")

text = Text(bot, message)
text.set_content(templates.INFO_MESSAGE.format(name=name, id=id))

text.send(parse_mode="MarkdownV2")
28 changes: 19 additions & 9 deletions app/handlers/inline_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,26 @@
from telebot import TeleBot
from telebot import types

from app.services.text_service import generate_random_text
from app.services.audio_service import generate_random_mix
from app.models.text import Text
from app.models.voice import Voice

def inline_dispatch(inline_query, bot: TeleBot):
def inline_text(inline_query, bot: TeleBot, data:dict):
options = data['options']
text = Text()
voice = Voice()
try:
text = types.InlineQueryResultArticle('1', 'Random Text',
types.InputTextMessageContent(generate_random_text()))
audio = types.InlineQueryResultArticle('2',
'TODO',
types.InputTextMessageContent("TODO"))
bot.answer_inline_query(inline_query.id, [text, audio], cache_time=0)
text = types.InlineQueryResultArticle('2', 'Random Text',
types.InputTextMessageContent(text.generate(options).content()))
bot.answer_inline_query(inline_query.id, [text], cache_time=0)
except Exception as e:
print(e)

def inline_voice(inline_query, bot:TeleBot, data:dict):
options = data['options']
voice = Voice().generate(options).to_voice().content()
try:
voice = types.InlineQueryResultVoice('3', title = 'Random Voice',
voice_url=voice)
bot.answer_inline_query(inline_query.id, [voice], cache_time=0)
except Exception as e:
print(e)
12 changes: 9 additions & 3 deletions app/handlers/member_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
from telebot import TeleBot
from telebot.types import Message

from app.services.member_service import generate_welcome
from app.models.text import Text
from configs import templates

# invoke when new member joins
def get_welcome(message:Message, bot:TeleBot):
# typing ...
print("welcome")
bot.send_chat_action(message.chat.id, "typing")
new_member = message.new_chat_members
welcome = generate_welcome(new_member[0].first_name, new_member[0].id)
bot.send_message(message.chat.id, welcome, parse_mode="MarkdownV2")
welcome = templates.WELCOME_MESSAGE.format(name=new_member[0].first_name,
id=new_member[0].id)
text = Text(bot, message)
text.set_content(welcome)
text.send(parse_mode="MarkdownV2")

30 changes: 16 additions & 14 deletions app/handlers/text_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@
from telebot import TeleBot
from telebot.types import Message

from app.services.text_service import generate_random_text
import string
from app.models.text import Text

SPECIAL = ['_', '*', '[', ']', '(', ')', '~', '`', '<' , '>', '#', '+', '-', '=', '|', '{', '}', '.', '!' ]

# invoke with "/random" command
def get_random_text(message: Message, bot: TeleBot):
# invoke with "/text" command
def get_random_text(message: Message, bot: TeleBot, data:dict):
# pretend to be typing
bot.send_chat_action(message.chat.id, "typing")
bot.send_message(message.chat.id, generate_random_text())
# get options from message
options = data['options']

text = Text(bot, message)
text.generate(options).send()

# invoke with "/random_mono"
def get_random_text_mono(message: Message, bot: TeleBot):
# invoke with "/mono"
def get_random_text_mono(message: Message, bot: TeleBot, data:dict):
# pretend to be typing
bot.send_chat_action(message.chat.id, "typing")
text = generate_random_text()
# add an "\" before every special char for parsing markdown
for i in SPECIAL:
text = text.replace(i, "\\"+i)
bot.send_message(message.chat.id, "`"+text+"`", parse_mode="MarkdownV2")

options = data['options']

mono = Text(bot, message)
mono.generate(options)
mono.to_mono().send(parse_mode="MarkdownV2")
37 changes: 37 additions & 0 deletions app/middlewares/option_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from telebot import TeleBot
from telebot.handler_backends import BaseMiddleware
from telebot.types import InlineQuery
from telebot.types import Message

class OptionMiddleware(BaseMiddleware):
def __init__(self):
self.update_types = ['message', 'inline_query']

def pre_process(self, message, data):

# if it's an inline query
if isinstance(message, InlineQuery):
msg_text = message.query
if msg_text == "":
msg_text = "*"
if msg_text[0] == '/':
options = msg_text.split(" ",1)
options.pop(0)
else:
options = [msg_text]

# if it's a message
elif isinstance(message, Message):
msg_text = message.text
options = msg_text.split(" ",1)
options.pop(0)

if options:
# remove spaces and make options list
options = options[0].replace(" ","")
options = options.split(",")

data['options'] = options

def post_process(self, message, data, exception=None):
pass
File renamed without changes.
139 changes: 139 additions & 0 deletions app/models/audio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
"""
audio.py
the model for audio messages
"""
import wave
import random
import struct
import uuid
import math
import ffmpeg

from app.models.base import Base
from app.models.msg_type import MsgType

from configs import dirs
from configs import env

class Audio(Base):

MSG_TYPE:str = MsgType.AUDIO

HOST_PREFIX = "https://{}:{}".format(env.WEBHOOK_HOST, env.WEBHOOK_PORT)

SAMPLE_RATE = 8000
FREQ_HIGH = 800
FREQ_LOW = 200
VOLUME_HIGH = 0.6
VOLUME_LOW = 0.3

OPTIONS_AVAILABLE = ['sine', 'noise']
OPTIONS_DEFAULT = {
"sine": True,
"noise": False
}

# generate a noise sample
@staticmethod
def noise_sample():
sample = random.randint(-32768, 32767)
sample_packed = struct.pack('h', sample)
return sample_packed

# generate a sine sample
@staticmethod
def sine_sample(freq, volume, x):
value = volume * math.sin(2 * math.pi * freq * (x / Audio.SAMPLE_RATE))
sample = int(value * 32767.0)
return struct.pack('h', sample)

# mix up according to options
def _mix_up(self, len):
values = []
for i in range(0, len):
if self._options['sine']:
# two channels
freq_channel_1 = random.randint(Audio.FREQ_LOW, Audio.FREQ_HIGH)
freq_channel_2 = random.randint(Audio.FREQ_LOW, Audio.FREQ_HIGH)
#volume
volume_channel_1 = random.uniform(Audio.VOLUME_LOW, Audio.VOLUME_HIGH)
volume_channel_2 = random.uniform(Audio.VOLUME_LOW, Audio.VOLUME_HIGH)
# build the chunk
# duration of every chunk
chunk_duration = random.randint(1, 3)
for x in range(0, chunk_duration * Audio.SAMPLE_RATE):
if self._options['sine']:
sine_channel_1 = Audio.sine_sample(freq_channel_1, volume_channel_1, x)
sine_channel_2 = Audio.sine_sample(freq_channel_2, volume_channel_2, x)
if self._options['noise']:
noise_channel_1 = Audio.noise_sample()
noise_channel_2 = Audio.noise_sample()

# build into values
if self._options['sine'] and self._options['noise']:
values.append(random.choice([sine_channel_1, noise_channel_1]))
values.append(random.choice([sine_channel_2, noise_channel_2]))
elif self._options['sine'] and not self._options['noise']:
values.append(sine_channel_1)
values.append(sine_channel_2)
elif self._options['noise'] and not self._options['sine']:
values.append(noise_channel_1)
values.append(noise_channel_2)

# make a byte string
values_str = b''.join(values)
return values_str

# generate an audio
def generate(self, options=None):
name = uuid.uuid4().hex
# add prefix to file name
name_prefix = "/sine-"
# set options
self._options = dict(Audio.OPTIONS_DEFAULT)
if options and not options == ['']:
name_prefix = "/"
self._options = dict.fromkeys(Audio.OPTIONS_AVAILABLE, False)
for option in options:
if option in Audio.OPTIONS_AVAILABLE:
self._options[option] = True
name_prefix = name_prefix + option + "-"

# if options are empty now, return to default
if self._options == dict.fromkeys(Audio.OPTIONS_AVAILABLE, False):
self._options = dict(Audio.OPTIONS_DEFAULT)
name_prefix += "sine-"

self._filename = name_prefix+name+".wav"
self._filepath = dirs.AUDIO_DIR+self._filename
# open the audio file
audio = wave.open(self._filepath, "wb")
audio.setparams((2, 2, 24000, 0, 'NONE', 'not compressed'))

# generate random length
len = random.randint(8, 10)

values_str = self._mix_up(len)

audio.writeframes(values_str)
audio.close()

self._content = Audio.HOST_PREFIX+"/storage/audio"+self._filename

return self

# convert to mpeg for telegran server to download
def to_mpeg(self):
wavfile = self._filepath
mpegfile_name = self._filename+".mp3"
mpegfile = dirs.AUDIO_DIR + mpegfile_name
(
ffmpeg
.input(wavfile, format="wav")
.output(mpegfile)
.run()
)
self._filename = mpegfile_name
self._filepath = dirs.AUDIO_DIR+self._filename
self._content = Audio.HOST_PREFIX+"/storage/audio"+self._filename
return self
Loading

0 comments on commit e005270

Please sign in to comment.