1
0

fix: use async modules

fix: duplicate queue problem
This commit is contained in:
オスカー、 2023-11-27 21:22:51 +09:00
parent 54991608d8
commit e3d5a2f5f4
No known key found for this signature in database
GPG Key ID: B1EFBBF5C93FF78F
6 changed files with 55 additions and 50 deletions

1
.gitignore vendored
View File

@ -27,6 +27,7 @@ share/python-wheels/
MANIFEST MANIFEST
.DS_Store .DS_Store
config.json config.json
credentials.json
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template

View File

@ -18,33 +18,13 @@ Google 스프레드시트를 기반으로 지정된 문구들을 자동적으로
## Options ## Options
config.example.josn 파일을 복사 후 config.json으로 이름 변경 후 작성해주세요. config.example.josn 파일을 복사 후 config.json으로 이름 변경 후 작성해주세요.
```json
{
"token": "여기에는 토큰을 넣어주세요",
// 봇을 실행할 계정에서 "설정 -> 기타 설정의 API -> 액세스 토큰 생성 -> `노트를 작성하거나 삭제합니다` 체크 후 나온 값"
"origin": "여기에는 서버 주소를 넣어주세요",
// (k.lapy.link, phater.live... etc)
"max_duplicate": 3,
// 중복으로 처리할 대사의 최대 수
"rate": 60,
// 자동 노트 게시 간격 (분 단위)
"visibility": "home",
// 공개 범위 (public, home, followers, specified)
"worksheet": "여기에는 구글 스프레드시트 주소를 넣어주세요",
"template": {
"auto": "{text}",
// 자동으로 게시되는 노트의 템플릿 {text} = 내용 {from} = 예시 시트 기준 "대사 위치" {number} = 예시 시트 기준 "대사 번호" (꼭 숫자일 필요 없음)
"mention": "{text}\n \n<small>{from}에서 발췌됨. ({number}번 대사)</small>"
// 답장으로 게시되는 노트의 템플릿 {text} = 내용 {from} = 예시 시트 기준 "대사 위치" {number} = 예시 시트 기준 "대사 번호" (꼭 숫자일 필요 없음)
}
}
```
## How ## How
### Requirements ### Requirements
* Git * Git
* Google Cloud Service Account * Google Cloud Service Account
* 구글 스프레드시트 연동을 위해 작업이 필요합니다. ([gspread 문서 참조](https://docs.gspread.org/en/latest/oauth2.html)) * 구글 스프레드시트 연동을 위해 작업이 필요합니다. ([gspread 문서 참조](https://docs.gspread.org/en/latest/oauth2.html))
* 생성한 Service Account 인증 json 키 파일의 위치를 config.json에 작성해주세요.
* Ubuntu 20.04+ or Windows 10+ * Ubuntu 20.04+ or Windows 10+
* Python 3.10+ * Python 3.10+
* Google 스프레드시트 ([예시 스프레드시트](https://docs.google.com/spreadsheets/d/1nO70lwFFkyyK8AtVE4fWO7lW7KDtM5pNudGJydTaQdk/edit)) * Google 스프레드시트 ([예시 스프레드시트](https://docs.google.com/spreadsheets/d/1nO70lwFFkyyK8AtVE4fWO7lW7KDtM5pNudGJydTaQdk/edit))

View File

@ -1,6 +1,7 @@
{ {
"token": "YOUR_ACCESS_TOKEN_HERE", "token": "YOUR_ACCESS_TOKEN_HERE",
"origin": "YOUR_MISSKEY_SERVER_HERE", "origin": "YOUR_MISSKEY_SERVER_HERE",
"credentialsJSONFile": "./credentials.json",
"duplicateQueueAfter": 3, "duplicateQueueAfter": 3,
"rate": 60, "rate": 60,
"startFrom": 30, "startFrom": 30,

View File

@ -14,13 +14,13 @@ class Post(commands.Cog):
@tasks.loop(seconds=1800) @tasks.loop(seconds=1800)
async def _postLine(self) -> None: async def _postLine(self) -> None:
line = self.bot.get_random_line() line = await self.bot.get_random_line()
while line.text in self.posted: while line.text in self.posted:
line = self.bot.get_random_line() line = await self.bot.get_random_line()
template = self.bot.config.note template = self.bot.config.note
result = template.replace("{text}", line.text).replace("{from}", line.where).replace("{number}", line.number) result = template.replace("{text}", line.text).replace("{from}", line.where).replace("{number}", line.number)
await self.bot.client.note.action.send(content=result, visibility=self.visibility) await self.bot.client.note.action.send(content=result, visibility=self.visibility)
self.posted.append(line) self.posted.append(line.text)
if len(self.posted) > self.max_count: if len(self.posted) > self.max_count:
self.posted.pop(0) self.posted.pop(0)
@ -37,3 +37,5 @@ async def setup(bot: Bot):
await asyncio.sleep(1) await asyncio.sleep(1)
now = datetime.now() now = datetime.now()
await cog._postLine.start() await cog._postLine.start()
else:
await cog._postLine.start()

71
main.py
View File

@ -2,9 +2,10 @@ import asyncio
import json import json
import random import random
import gspread import gspread_asyncio
from aiohttp import ClientWebSocketResponse from aiohttp import ClientWebSocketResponse
from gspread.worksheet import Worksheet from google.oauth2.service_account import Credentials
from gspread_asyncio import AsyncioGspreadWorksheet as Worksheet
from mipac.models.notification import NotificationNote from mipac.models.notification import NotificationNote
from mipa.ext import commands from mipa.ext import commands
@ -15,6 +16,7 @@ class Config:
raw = json.load(file) raw = json.load(file)
self.token = raw.get("token") self.token = raw.get("token")
self.origin = raw.get("origin") self.origin = raw.get("origin")
self.credentials = raw.get("credentialsJSONFile")
self.max = raw.get("duplicateQueueAfter") self.max = raw.get("duplicateQueueAfter")
self.rate = raw.get("rate") self.rate = raw.get("rate")
self.start_time = raw.get("startFrom") self.start_time = raw.get("startFrom")
@ -26,53 +28,72 @@ class Config:
if any([ if any([
self.token is None, self.token is None,
self.origin is None, self.origin is None,
self.credentials is None,
self.worksheet is None, self.worksheet is None,
self.note is None, self.note is None,
self.reply is None self.reply is None
]): ]):
raise ValueError("config.json 파일에 일부 필수 값이 누락되었습니다.") raise ValueError("config.json 파일에 일부 필수 값이 누락되었습니다.")
def get_creds(self):
creds = Credentials.from_service_account_file(self.credentials)
scoped = creds.with_scopes([
"https://spreadsheets.google.com/feeds",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive",
])
return scoped
class Line: class Line:
def __init__(self, row: int, bot: "Autoposter") -> None: def __init__(self, data: dict) -> None:
sheet = bot.get_worksheet() self.location = data["row"] + 2
self.location = row self.text = data["text"]
res = sheet.get(f"D{row}") self.where = data["where"]
self.text = res[0][0].strip() self.number = data["number"]
res = sheet.get(f"C{row}")
self.where = res[0][0].strip() @classmethod
res = sheet.get(f"B{row}") async def from_number(cls: "Line", row: int, sheet: Worksheet) -> "Line":
self.number = res[0][0].strip() res = await sheet.get(f"D{row}")
text = res[0][0].strip()
res = await sheet.get(f"C{row}")
where = res[0][0].strip()
res = await sheet.get(f"B{row}")
number = res[0][0].strip()
data = {"row": row, "number": number, "where": where, "text": text}
return cls(data)
class Autoposter(commands.Bot): class Autoposter(commands.Bot):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.config: Config = Config("./config.json") self.config: Config = Config("./config.json")
self.agcm = gspread_asyncio.AsyncioGspreadClientManager(self.config.get_creds)
def get_worksheet(self) -> Worksheet: async def get_worksheet(self) -> Worksheet:
gc = gspread.service_account() client = await self.agcm.authorize()
sh = gc.open_by_url(self.config.worksheet) spreadsheet = await client.open_by_url(self.config.worksheet)
worksheet = sh.get_worksheet(0) worksheet = await spreadsheet.get_worksheet(0)
return worksheet return worksheet
def get_random_line(self) -> str: async def get_random_line(self) -> Line:
sheet: Worksheet = self.get_worksheet() sheet: Worksheet = await self.get_worksheet()
response = sheet.get("F4") response = await sheet.get("F4")
if response is None or response == "": if response is None or response == "":
return return
count = int(response[0][0]) count = int(response[0][0])
result = random.randint(1, count) result = random.randint(1, count)
number = result + 2 number = result + 2
return Line(number, self) return await Line.from_number(number, sheet)
def get_line(self, number: int) -> str: async def get_line(self, number: int) -> Line:
return Line(number, self) sheet: Worksheet = await self.get_worksheet()
return await Line.from_number(number, sheet)
async def _connect_channel(self): async def _connect_channel(self) -> None:
await self.router.connect_channel(['main', 'global']) await self.router.connect_channel(['main', 'global'])
async def on_ready(self, ws: ClientWebSocketResponse): async def on_ready(self, ws: ClientWebSocketResponse) -> None:
print(f"Connected as @{self.user.username}@{self.config.origin}") print(f"Connected as @{self.user.username}@{self.config.origin}")
await self._connect_channel() await self._connect_channel()
extensions = [ extensions = [
@ -81,11 +102,11 @@ class Autoposter(commands.Bot):
for extension in extensions: for extension in extensions:
await self.load_extension(extension) await self.load_extension(extension)
async def on_reconnect(self, ws: ClientWebSocketResponse): async def on_reconnect(self, ws: ClientWebSocketResponse) -> None:
print("Disconnected from server. Reconnecting...") print("Disconnected from server. Reconnecting...")
await self._connect_channel() await self._connect_channel()
async def on_mention(self, notice: NotificationNote): async def on_mention(self, notice: NotificationNote) -> None:
if notice.note.reply_id is not None: if notice.note.reply_id is not None:
return return

View File

@ -1,3 +1,3 @@
gspread gspread-asyncio
mipa mipa
python-dotenv python-dotenv