fix: use async modules
fix: duplicate queue problem
This commit is contained in:
parent
54991608d8
commit
e3d5a2f5f4
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||||
|
22
README.md
22
README.md
@ -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))
|
||||||
|
@ -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,
|
||||||
|
@ -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
71
main.py
@ -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
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
gspread
|
gspread-asyncio
|
||||||
mipa
|
mipa
|
||||||
python-dotenv
|
python-dotenv
|
Loading…
Reference in New Issue
Block a user