Skip to content
This repository has been archived by the owner on Jun 13, 2022. It is now read-only.

Commit

Permalink
Merge pull request #98 from sizumita/release/1.2.4
Browse files Browse the repository at this point in the history
Release 1.2.4
  • Loading branch information
sizumita authored Apr 26, 2021
2 parents d85a02f + f8da691 commit f642225
Show file tree
Hide file tree
Showing 15 changed files with 267 additions and 25 deletions.
32 changes: 27 additions & 5 deletions .github/workflows/publish-image-test.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
name: Build Test Docker

on:
push:
branches: [ develop ]
pull_request:
branches: [ develop ]

jobs:
build_test:
runs-on: ubuntu-latest
env:
IMAGE_NAME: minimaid
steps:
- name: checkout
uses: actions/checkout@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Build Test
- name: Set up Docker Buildx Builder
run: |
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap
docker buildx build \
--platform linux/amd64 .
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build Test
uses: docker/build-push-action@v2
with:
platforms: linux/amd64
context: .
push: false
tags: |
ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new

- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
28 changes: 24 additions & 4 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ name: Build and Publish Docker

on:
push:
branches:
- master
branches-ignore:
- '**'
tags:
- '*'

jobs:
build_and_push:
Expand All @@ -14,6 +16,10 @@ jobs:
- name: checkout
uses: actions/checkout@v2

- name: Get tag
id: tag
uses: dawidd6/action-get-tag@v1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

Expand All @@ -30,13 +36,27 @@ jobs:
docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build and push
uses: docker/build-push-action@v2
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
platforms: linux/amd64,linux/arm64,linux/arm/v7
context: .
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest
ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:1.2.3
ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{steps.tag.outputs.tag}}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new

- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
4 changes: 4 additions & 0 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def __init__(self) -> None:
)
self.db = Database()

async def on_ready(self) -> None:
prefix = environ["PREFIX"]
await self.change_presence(activity=discord.Game(name=f"prefix: {prefix}"))

async def on_error(self, event_method: str, *args: Any, **kwargs: Any) -> None:
_, err, _ = sys.exc_info()
if isinstance(err, MiniMaidException) and event_method == "on_message":
Expand Down
6 changes: 6 additions & 0 deletions cogs/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ async def replay_audio(self, ctx: Context) -> None:
try:
await ctx.success("30秒前からのクリップを作成します...")
file = await ctx.voice_client.replay()
if file is None:
await ctx.error("エラーが発生しました。もしエラーが再発するようであれば再接続してください。")
return
timestamp = datetime.utcnow().timestamp()
file.seek(0)
await ctx.send("作成終了しました。", file=discord.File(file, f"{timestamp}.wav"))
Expand Down Expand Up @@ -327,6 +330,9 @@ async def record_start(self, ctx: Context) -> None:
try:
await ctx.success("録音開始します...")
file = await ctx.voice_client.record()
if file is None:
await ctx.error("エラーが発生しました。もしエラーが再発するようであれば再接続してください。")
return
await ctx.success("録音終了しました。")
timestamp = datetime.utcnow().timestamp()
file.seek(0)
Expand Down
29 changes: 29 additions & 0 deletions cogs/help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from typing import TYPE_CHECKING

from discord.ext.commands import (
Cog,
group
)

from lib.context import Context
from lib.embed import help_embed

if TYPE_CHECKING:
from bot import MiniMaid


class HelpCog(Cog):
def __init__(self, bot: 'MiniMaid') -> None:
self.bot = bot

@group(name="help", invoke_without_command=True)
async def help_command(self, ctx: Context) -> None:
await ctx.embed(help_embed())

@group(name="ping", invoke_without_command=True)
async def ping(self, ctx: Context) -> None:
await ctx.success("pong!")


def setup(bot: 'MiniMaid') -> None:
return bot.add_cog(HelpCog(bot))
27 changes: 25 additions & 2 deletions lib/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
import asyncio


def remove_header(content: bytes) -> io.BytesIO:
def make_pcm(content: bytes) -> io.BytesIO:
"""
wavのファイルからヘッダーを取り除き、フレームレートなどを合わせます。
:param content: wavのデータ
:return: 出力するPCM
"""
with wave.open(io.BytesIO(content)) as wav:
bit = wav.getsampwidth()
pcm = wav.readframes(wav.getnframes())
Expand All @@ -24,6 +29,11 @@ def remove_header(content: bytes) -> io.BytesIO:


def mp3_to_pcm(raw: bytes) -> io.BytesIO:
"""
MP3のデータをPCMに変換します。
:param raw: MP3のデータ
:return: 出力するPCM
"""
mp3 = Mpg123()
mp3.feed(raw)
rate, channels, encoding = mp3.get_format()
Expand All @@ -42,12 +52,25 @@ def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
self.executor = ThreadPoolExecutor()

async def to_pcm(self, raw: bytes, filetype: str) -> io.BytesIO:
"""
データをPCMに変換します。
:param raw: 変換するデータ
:param filetype: 変換するデータのタイプ
:return: 出力するPCM
"""
if filetype == "mp3":
return await self.loop.run_in_executor(self.executor, partial(mp3_to_pcm, raw))
else:
return await self.loop.run_in_executor(self.executor, partial(remove_header, raw))
return await self.loop.run_in_executor(self.executor, partial(make_pcm, raw))

async def create_source(self, attachment: discord.Attachment) -> discord.AudioSource:
"""
Attachmentからdiscord.PCMAudioを作成します。
:param attachment: 変換するアタッチメント
:return: 出力するPCMAudio
"""
raw = await attachment.read()

data = await self.to_pcm(raw, "mp3" if attachment.filename.endswith(".mp3") else "wav")
Expand Down
15 changes: 15 additions & 0 deletions lib/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@


def bot_connected_only() -> Any:
"""
BotがVCに接続しているかのチェック
:return: check
"""
def predicate(ctx: Context) -> bool:
if ctx.voice_client is None:
raise BotNotConnected()
Expand All @@ -17,6 +22,11 @@ def predicate(ctx: Context) -> bool:


def user_connected_only() -> Any:
"""
コマンドを打ったユーザーがVCに接続しているかのチェック
:return: check
"""
def predicate(ctx: Context) -> bool:
if ctx.author.voice is None or ctx.author.voice.channel is None:
raise UserNotConnected()
Expand All @@ -26,6 +36,11 @@ def predicate(ctx: Context) -> bool:


def voice_channel_only() -> Any:
"""
ユーザーが接続しているチャンネルがVCであるかのチェック
:return: check
"""
def predicate(ctx: Context) -> bool:
if isinstance(ctx.author.voice.channel, discord.StageChannel):
raise NoStageChannel()
Expand Down
20 changes: 20 additions & 0 deletions lib/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,38 @@ def __init__(self, **kwargs: dict) -> None:
super(Context, self).__init__(**kwargs)

async def error(self, content: str, description: Optional[str] = None) -> discord.Message:
"""
エラー表示のための関数
:param content: エラーのタイトル
:param description: エラーの詳細
:return: 送信したメッセージ
"""
embed = discord.Embed(title=f"\U000026a0 {content}", color=0xffc107)
if description is not None:
embed.description = description

return await self.send(embed=embed)

async def success(self, content: str, description: Optional[str] = None) -> discord.Message:
"""
コマンドの実行などに成功したときのための関数
:param content: タイトル
:param description: 内容
:return: 送信したメッセージ
"""
embed = discord.Embed(title=f"\U00002705 {content}", colour=discord.Colour.green())
if description is not None:
embed.description = description

return await self.send(embed=embed)

async def embed(self, embed: discord.Embed) -> discord.Message:
"""
Embedのみを送信する関数
:param embed: 送信するEmbed
:return: 送信したメッセージ
"""
return await self.send(embed=embed)
35 changes: 28 additions & 7 deletions lib/discord/buffer_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import time
from collections import defaultdict
from itertools import zip_longest
import logging

from .opus import Decoder
from .opus import Decoder, OpusError

logger = logging.getLogger(__name__)


class PacketBase:
Expand Down Expand Up @@ -51,8 +54,12 @@ def calc_extention_header_length(self, data: bytes) -> None:
continue
offset += 1 + (0b1111 & (byte_ >> 4))

if self.decrypted[offset + 1] in [0, 2]:
offset += 1
try:
if self.decrypted[offset + 1] in [0, 2]:
offset += 1
except IndexError:
self.decrypted = None
return
self.decrypted = data[offset + 1:]

@property
Expand Down Expand Up @@ -135,11 +142,10 @@ def is_speaker(self, ssrc: int) -> bool:
return ssrc in self.ssrc.keys()

def add_ssrc(self, data: dict) -> None:
if len(self.ssrc) >= 15:
return
self.ssrc[data["ssrc"]] = data["user_id"]

async def decode(self):

file = BytesIO()
wav = wave.open(file, "wb")
wav.setnchannels(Decoder.CHANNELS)
Expand All @@ -151,7 +157,11 @@ async def decode(self):
if c > 15:
break
queue = PacketQueue(packets)
pcm: ResultPCM = await self.decode_one(queue)
try:
pcm: ResultPCM = await self.decode_one(queue)
except OpusError:
wav.close()
return None
pcm_list.append(pcm)
c += 1
pcm_list.sort(key=lambda x: x.start_time)
Expand Down Expand Up @@ -241,6 +251,12 @@ async def decode_one(self, queue: PacketQueue):
else:
start_time = min(packet.real_time, start_time)

if packet.decrypted is None:
data = decoder.decode_float(packet.decrypted)
pcm += data
last_timestamp = packet.timestamp
continue

if len(packet.decrypted) < 10:
last_timestamp = packet.timestamp
continue
Expand All @@ -251,7 +267,12 @@ async def decode_one(self, queue: PacketQueue):
margin = [0] * 2 * int(Decoder.SAMPLE_SIZE * (elapsed - 0.02) * Decoder.SAMPLING_RATE)
# await self.loop.run_in_executor(self.executor, partial(pcm.append, margin))
pcm += margin
data = decoder.decode_float(packet.decrypted)
try:
data = decoder.decode_float(packet.decrypted)
except Exception:
logger.error(f"{packet.cc=}")
logger.error(f"{packet.extend=}")
raise
pcm += data
last_timestamp = packet.timestamp

Expand Down
5 changes: 3 additions & 2 deletions lib/discord/voice_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from io import BytesIO
from typing import Optional

from discord import VoiceClient
from lib.discord.websocket import MiniMaidVoiceWebSocket
Expand All @@ -13,8 +14,8 @@ async def connect_websocket(self) -> MiniMaidVoiceWebSocket:
self._connected.set()
return ws

async def record(self) -> BytesIO:
async def record(self) -> Optional[BytesIO]:
return await self.ws.record(self.client)

async def replay(self) -> BytesIO:
async def replay(self) -> Optional[BytesIO]:
return await self.ws.replay()
Loading

0 comments on commit f642225

Please sign in to comment.