LeafLeafLeaf commited on
Commit
1d20b52
·
1 Parent(s): f371e42

fix: 同步https://github.com/MeetWq/meme-generator/releases/tag/v0.0.20

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +1 -0
  2. .pre-commit-config.yaml +2 -13
  3. Dockerfile +17 -17
  4. Dockerfile.memebot +58 -0
  5. docker/start_memebot.sh +7 -0
  6. meme_generator/app.py +15 -15
  7. meme_generator/cli.py +6 -6
  8. meme_generator/config.py +4 -4
  9. meme_generator/dirs.py +2 -3
  10. meme_generator/download.py +4 -5
  11. meme_generator/manager.py +7 -7
  12. meme_generator/meme.py +17 -20
  13. meme_generator/memes/5000choyen/__init__.py +7 -9
  14. meme_generator/memes/ace_attorney_dialog/__init__.py +119 -0
  15. meme_generator/memes/ace_attorney_dialog/images/bubble.png +3 -0
  16. meme_generator/memes/ace_attorney_dialog/images/mark.png +3 -0
  17. meme_generator/memes/acg_entrance/__init__.py +1 -2
  18. meme_generator/memes/add_chaos/__init__.py +1 -2
  19. meme_generator/memes/addiction/__init__.py +1 -2
  20. meme_generator/memes/alike/__init__.py +1 -3
  21. meme_generator/memes/always/__init__.py +5 -3
  22. meme_generator/memes/always_like/__init__.py +1 -2
  23. meme_generator/memes/anti_kidnap/__init__.py +1 -2
  24. meme_generator/memes/anya_suki/__init__.py +1 -2
  25. meme_generator/memes/applaud/__init__.py +2 -3
  26. meme_generator/memes/ascension/__init__.py +1 -2
  27. meme_generator/memes/ask/__init__.py +1 -3
  28. meme_generator/memes/back_to_work/__init__.py +6 -3
  29. meme_generator/memes/bad_news/__init__.py +1 -2
  30. meme_generator/memes/beat_head/__init__.py +2 -3
  31. meme_generator/memes/beat_up/__init__.py +27 -0
  32. meme_generator/memes/beat_up/images/0.png +3 -0
  33. meme_generator/memes/beat_up/images/1.png +3 -0
  34. meme_generator/memes/beat_up/images/2.png +3 -0
  35. meme_generator/memes/bite/__init__.py +2 -3
  36. meme_generator/memes/blood_pressure/__init__.py +4 -3
  37. meme_generator/memes/bluearchive/__init__.py +1 -2
  38. meme_generator/memes/bocchi_draft/__init__.py +5 -4
  39. meme_generator/memes/bronya_holdsign/__init__.py +1 -2
  40. meme_generator/memes/bubble_tea/__init__.py +8 -4
  41. meme_generator/memes/call_110/__init__.py +1 -3
  42. meme_generator/memes/caoshen_bite/__init__.py +2 -3
  43. meme_generator/memes/capoo_draw/__init__.py +2 -3
  44. meme_generator/memes/capoo_rip/__init__.py +2 -3
  45. meme_generator/memes/capoo_rub/__init__.py +5 -4
  46. meme_generator/memes/capoo_say/__init__.py +3 -4
  47. meme_generator/memes/capoo_strike/__init__.py +1 -2
  48. meme_generator/memes/captain/__init__.py +1 -2
  49. meme_generator/memes/certificate/__init__.py +4 -3
  50. meme_generator/memes/charpic/__init__.py +1 -3
.gitignore CHANGED
@@ -4,6 +4,7 @@ dist/
4
  .idea/
5
  venv/
6
  .venv/
 
7
  pdm.lock
8
 
9
  result.png
 
4
  .idea/
5
  venv/
6
  .venv/
7
+ node_modules/
8
  pdm.lock
9
 
10
  result.png
.pre-commit-config.yaml CHANGED
@@ -6,23 +6,12 @@ ci:
6
  autoupdate_commit_msg: "chore: auto update by pre-commit hooks"
7
  repos:
8
  - repo: https://github.com/astral-sh/ruff-pre-commit
9
- rev: v0.1.9
10
  hooks:
11
  - id: ruff
12
  args: [--fix, --exit-non-zero-on-fix]
13
  stages: [commit]
14
-
15
- - repo: https://github.com/pycqa/isort
16
- rev: 5.13.2
17
- hooks:
18
- - id: isort
19
- stages: [commit]
20
-
21
- - repo: https://github.com/psf/black
22
- rev: 23.12.1
23
- hooks:
24
- - id: black
25
- stages: [commit]
26
 
27
  - repo: https://github.com/pre-commit/mirrors-prettier
28
  rev: v4.0.0-alpha.8
 
6
  autoupdate_commit_msg: "chore: auto update by pre-commit hooks"
7
  repos:
8
  - repo: https://github.com/astral-sh/ruff-pre-commit
9
+ rev: v0.3.5
10
  hooks:
11
  - id: ruff
12
  args: [--fix, --exit-non-zero-on-fix]
13
  stages: [commit]
14
+ - id: ruff-format
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  - repo: https://github.com/pre-commit/mirrors-prettier
17
  rev: v4.0.0-alpha.8
Dockerfile CHANGED
@@ -2,12 +2,13 @@ FROM python:3.10 as tmp
2
 
3
  WORKDIR /tmp
4
 
 
 
5
  ENV PATH="${PATH}:/root/.local/bin"
6
 
7
  COPY ./pyproject.toml ./poetry.lock* /tmp/
8
- RUN pip install poetry \
9
- && poetry config virtualenvs.in-project true \
10
- && poetry install --only main --no-interaction --no-ansi
11
 
12
  FROM python:3.10-slim as app
13
 
@@ -17,20 +18,8 @@ EXPOSE 2233
17
 
18
  VOLUME /data
19
 
20
- COPY --from=tmp /tmp/.venv /app/.venv
21
-
22
- COPY ./resources/fonts/* /usr/share/fonts/meme-fonts/
23
- RUN apt-get update \
24
- && apt-get install -y --no-install-recommends locales fontconfig fonts-noto-cjk fonts-noto-color-emoji gettext \
25
- && localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 \
26
- && fc-cache -fv \
27
- && apt-get purge -y --auto-remove \
28
- && rm -rf /var/lib/apt/lists/*
29
-
30
  ENV TZ=Asia/Shanghai \
31
  LC_ALL=zh_CN.UTF-8 \
32
- PATH="/app/.venv/bin:${PATH}" \
33
- VIRTUAL_ENV="/app/.venv" \
34
  LOAD_BUILTIN_MEMES=true \
35
  MEME_DIRS="[\"/data/memes\"]" \
36
  MEME_DISABLED_LIST="[]" \
@@ -40,12 +29,23 @@ ENV TZ=Asia/Shanghai \
40
  BAIDU_TRANS_APIKEY="" \
41
  LOG_LEVEL="INFO"
42
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  COPY ./meme_generator /app/meme_generator
44
 
45
  COPY ./docker/config.toml.template /app/config.toml.template
46
  COPY ./docker/start.sh /app/start.sh
47
- RUN mkdir -p /.config
48
- RUN chmod -R 777 /.config
49
  RUN chmod +x /app/start.sh
 
50
 
51
  CMD ["/app/start.sh"]
 
2
 
3
  WORKDIR /tmp
4
 
5
+ RUN curl -sSL https://install.python-poetry.org | python -
6
+
7
  ENV PATH="${PATH}:/root/.local/bin"
8
 
9
  COPY ./pyproject.toml ./poetry.lock* /tmp/
10
+
11
+ RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
 
12
 
13
  FROM python:3.10-slim as app
14
 
 
18
 
19
  VOLUME /data
20
 
 
 
 
 
 
 
 
 
 
 
21
  ENV TZ=Asia/Shanghai \
22
  LC_ALL=zh_CN.UTF-8 \
 
 
23
  LOAD_BUILTIN_MEMES=true \
24
  MEME_DIRS="[\"/data/memes\"]" \
25
  MEME_DISABLED_LIST="[]" \
 
29
  BAIDU_TRANS_APIKEY="" \
30
  LOG_LEVEL="INFO"
31
 
32
+ COPY --from=tmp /tmp/requirements.txt /app/requirements.txt
33
+
34
+ COPY ./resources/fonts/* /usr/share/fonts/meme-fonts/
35
+
36
+ RUN apt-get update \
37
+ && apt-get install -y --no-install-recommends locales fontconfig fonts-noto-color-emoji gettext \
38
+ && localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 \
39
+ && fc-cache -fv \
40
+ && apt-get purge -y --auto-remove \
41
+ && rm -rf /var/lib/apt/lists/* \
42
+ && pip install --no-cache-dir --upgrade -r /app/requirements.txt
43
+
44
  COPY ./meme_generator /app/meme_generator
45
 
46
  COPY ./docker/config.toml.template /app/config.toml.template
47
  COPY ./docker/start.sh /app/start.sh
 
 
48
  RUN chmod +x /app/start.sh
49
+ RUN python -m meme_generator.cli
50
 
51
  CMD ["/app/start.sh"]
Dockerfile.memebot ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10 as tmp
2
+
3
+ WORKDIR /tmp
4
+
5
+ RUN curl -sSL https://install.python-poetry.org | python -
6
+
7
+ ENV PATH="${PATH}:/root/.local/bin"
8
+
9
+ COPY ./pyproject.toml ./poetry.lock* /tmp/
10
+
11
+ RUN apt-get update \
12
+ && apt-get install -y --no-install-recommends git \
13
+ && poetry export -f requirements.txt --output requirements.txt --without-hashes \
14
+ && git clone --depth=1 https://github.com/MeetWq/github-meme-bot
15
+
16
+ FROM python:3.10-slim as app
17
+
18
+ WORKDIR /app
19
+
20
+ EXPOSE 2233
21
+
22
+ VOLUME /data
23
+
24
+ ENV TZ=Asia/Shanghai \
25
+ LC_ALL=zh_CN.UTF-8 \
26
+ LOAD_BUILTIN_MEMES=true \
27
+ MEME_DIRS="[\"/data/memes\"]" \
28
+ MEME_DISABLED_LIST="[]" \
29
+ GIF_MAX_SIZE=10.0 \
30
+ GIF_MAX_FRAMES=100 \
31
+ BAIDU_TRANS_APPID="" \
32
+ BAIDU_TRANS_APIKEY="" \
33
+ LOG_LEVEL="INFO"
34
+
35
+ COPY --from=tmp /tmp/requirements.txt /app/requirements.txt
36
+ COPY --from=tmp /tmp/github-meme-bot/src /app/src
37
+ COPY --from=tmp /tmp/github-meme-bot/bot.py /app/bot.py
38
+ COPY --from=tmp /tmp/github-meme-bot/.env /app/.env
39
+
40
+ COPY ./resources/fonts/* /usr/share/fonts/meme-fonts/
41
+
42
+ RUN apt-get update \
43
+ && apt-get install -y --no-install-recommends locales fontconfig fonts-noto-color-emoji gettext \
44
+ && localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 \
45
+ && fc-cache -fv \
46
+ && apt-get purge -y --auto-remove \
47
+ && rm -rf /var/lib/apt/lists/* \
48
+ && pip install --no-cache-dir --upgrade -r /app/requirements.txt \
49
+ && pip install --no-cache-dir --upgrade nonebot2 nonebot-adapter-github
50
+
51
+ COPY ./meme_generator /app/meme_generator
52
+
53
+ COPY ./docker/config.toml.template /app/config.toml.template
54
+ COPY ./docker/start_memebot.sh /app/start_memebot.sh
55
+ RUN chmod +x /app/start_memebot.sh
56
+ RUN python -m meme_generator.cli
57
+
58
+ CMD ["/app/start_memebot.sh"]
docker/start_memebot.sh ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ #! /usr/bin/env bash
2
+
3
+ mkdir -p ~/.config/meme_generator
4
+
5
+ envsubst < /app/config.toml.template > ~/.config/meme_generator/config.toml
6
+
7
+ exec python /app/bot.py
meme_generator/app.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, List, Literal, Optional, Tuple
2
 
3
  import filetype
4
  from fastapi import Depends, FastAPI, Form, HTTPException, Response, UploadFile
@@ -20,7 +20,7 @@ class MemeArgsResponse(BaseModel):
20
  type: str
21
  description: Optional[str] = None
22
  default: Optional[Any] = None
23
- enum: Optional[List[Any]] = None
24
 
25
 
26
  class MemeParamsResponse(BaseModel):
@@ -28,14 +28,14 @@ class MemeParamsResponse(BaseModel):
28
  max_images: int
29
  min_texts: int
30
  max_texts: int
31
- default_texts: List[str]
32
- args: List[MemeArgsResponse]
33
 
34
 
35
  class MemeInfoResponse(BaseModel):
36
  key: str
37
- keywords: List[str]
38
- patterns: List[str]
39
  params: MemeParamsResponse
40
 
41
 
@@ -56,11 +56,11 @@ def register_router(meme: Meme):
56
 
57
  @app.post(f"/memes/{meme.key}/")
58
  async def _(
59
- images: List[UploadFile] = [],
60
- texts: List[str] = meme.params_type.default_texts,
61
  args: args_model = Depends(args_checker), # type: ignore
62
  ):
63
- imgs: List[bytes] = []
64
  for image in images:
65
  imgs.append(await image.read())
66
 
@@ -94,16 +94,16 @@ default_meme_list = [
94
 
95
 
96
  class RenderMemeListRequest(BaseModel):
97
- meme_list: List[MemeKeyWithProperties] = default_meme_list
98
  order_direction: Literal["row", "column"] = "column"
99
  columns: int = 4
100
  column_align: Literal["left", "center", "right"] = "left"
101
- item_padding: Tuple[int, int] = (15, 2)
102
- image_padding: Tuple[int, int] = (50, 50)
103
  bg_color: ColorType = "white"
104
  fontsize: int = 30
105
  fontname: str = ""
106
- fallback_fonts: List[str] = []
107
 
108
 
109
  def register_routers():
@@ -158,7 +158,7 @@ def register_routers():
158
  if meme.params_type.args_type
159
  else MemeArgsModel
160
  )
161
- properties: Dict[str, Dict[str, Any]] = (
162
  args_model.schema().get("properties", {}).copy()
163
  )
164
  properties.pop("user_infos")
@@ -198,7 +198,7 @@ def register_routers():
198
  return Response(content=content, media_type=media_type)
199
 
200
  @app.post("/memes/{key}/parse_args")
201
- async def _(key: str, args: List[str] = []):
202
  try:
203
  meme = get_meme(key)
204
  return meme.parse_args(args)
 
1
+ from typing import Any, Literal, Optional
2
 
3
  import filetype
4
  from fastapi import Depends, FastAPI, Form, HTTPException, Response, UploadFile
 
20
  type: str
21
  description: Optional[str] = None
22
  default: Optional[Any] = None
23
+ enum: Optional[list[Any]] = None
24
 
25
 
26
  class MemeParamsResponse(BaseModel):
 
28
  max_images: int
29
  min_texts: int
30
  max_texts: int
31
+ default_texts: list[str]
32
+ args: list[MemeArgsResponse]
33
 
34
 
35
  class MemeInfoResponse(BaseModel):
36
  key: str
37
+ keywords: list[str]
38
+ patterns: list[str]
39
  params: MemeParamsResponse
40
 
41
 
 
56
 
57
  @app.post(f"/memes/{meme.key}/")
58
  async def _(
59
+ images: list[UploadFile] = [],
60
+ texts: list[str] = meme.params_type.default_texts,
61
  args: args_model = Depends(args_checker), # type: ignore
62
  ):
63
+ imgs: list[bytes] = []
64
  for image in images:
65
  imgs.append(await image.read())
66
 
 
94
 
95
 
96
  class RenderMemeListRequest(BaseModel):
97
+ meme_list: list[MemeKeyWithProperties] = default_meme_list
98
  order_direction: Literal["row", "column"] = "column"
99
  columns: int = 4
100
  column_align: Literal["left", "center", "right"] = "left"
101
+ item_padding: tuple[int, int] = (15, 2)
102
+ image_padding: tuple[int, int] = (50, 50)
103
  bg_color: ColorType = "white"
104
  fontsize: int = 30
105
  fontname: str = ""
106
+ fallback_fonts: list[str] = []
107
 
108
 
109
  def register_routers():
 
158
  if meme.params_type.args_type
159
  else MemeArgsModel
160
  )
161
+ properties: dict[str, dict[str, Any]] = (
162
  args_model.schema().get("properties", {}).copy()
163
  )
164
  properties.pop("user_infos")
 
198
  return Response(content=content, media_type=media_type)
199
 
200
  @app.post("/memes/{key}/parse_args")
201
+ async def _(key: str, args: list[str] = []):
202
  try:
203
  meme = get_meme(key)
204
  return meme.parse_args(args)
meme_generator/cli.py CHANGED
@@ -2,7 +2,7 @@ import asyncio
2
  import copy
3
  from argparse import ArgumentParser
4
  from pathlib import Path
5
- from typing import Any, Dict, List
6
 
7
  import filetype
8
 
@@ -80,7 +80,7 @@ def meme_info(key: str) -> str:
80
 
81
  default_texts = ", ".join([f'"{text}"' for text in meme.params_type.default_texts])
82
 
83
- def arg_info(name: str, info: Dict[str, Any]) -> str:
84
  text = (
85
  f' "{name}"\n'
86
  f" 描述:{info.get('description', '')}\n"
@@ -94,7 +94,7 @@ def meme_info(key: str) -> str:
94
 
95
  if args := meme.params_type.args_type:
96
  model = args.model
97
- properties: Dict[str, Dict[str, Any]] = model.schema().get("properties", {})
98
  properties.pop("user_infos")
99
  args_info = "\n" + "\n".join(
100
  [arg_info(name, info) for name, info in properties.items()]
@@ -134,7 +134,7 @@ def generate_meme_preview(key: str) -> str:
134
 
135
 
136
  def generate_meme(
137
- key: str, images: List[str], texts: List[str], args: Dict[str, Any]
138
  ) -> str:
139
  try:
140
  meme = get_meme(key)
@@ -180,8 +180,8 @@ def main():
180
  kwargs = vars(args)
181
  kwargs.pop("handle")
182
  key: str = kwargs.pop("key")
183
- images: List[str] = kwargs.pop("images")
184
- texts: List[str] = kwargs.pop("texts")
185
  print(generate_meme(key, images, texts, kwargs)) # noqa: T201
186
 
187
  elif handle in ["run", "start"]:
 
2
  import copy
3
  from argparse import ArgumentParser
4
  from pathlib import Path
5
+ from typing import Any
6
 
7
  import filetype
8
 
 
80
 
81
  default_texts = ", ".join([f'"{text}"' for text in meme.params_type.default_texts])
82
 
83
+ def arg_info(name: str, info: dict[str, Any]) -> str:
84
  text = (
85
  f' "{name}"\n'
86
  f" 描述:{info.get('description', '')}\n"
 
94
 
95
  if args := meme.params_type.args_type:
96
  model = args.model
97
+ properties: dict[str, dict[str, Any]] = model.schema().get("properties", {})
98
  properties.pop("user_infos")
99
  args_info = "\n" + "\n".join(
100
  [arg_info(name, info) for name, info in properties.items()]
 
134
 
135
 
136
  def generate_meme(
137
+ key: str, images: list[str], texts: list[str], args: dict[str, Any]
138
  ) -> str:
139
  try:
140
  meme = get_meme(key)
 
180
  kwargs = vars(args)
181
  kwargs.pop("handle")
182
  key: str = kwargs.pop("key")
183
+ images: list[str] = kwargs.pop("images")
184
+ texts: list[str] = kwargs.pop("texts")
185
  print(generate_meme(key, images, texts, kwargs)) # noqa: T201
186
 
187
  elif handle in ["run", "start"]:
meme_generator/config.py CHANGED
@@ -1,6 +1,6 @@
1
  import json
2
  from pathlib import Path
3
- from typing import List, Optional, Union
4
 
5
  import toml
6
  from pydantic import BaseModel, Extra
@@ -12,13 +12,13 @@ config_file_path = get_config_file("config.toml")
12
 
13
  class MemeConfig(BaseModel):
14
  load_builtin_memes: bool = True
15
- meme_dirs: List[Path] = []
16
- meme_disabled_list: List[str] = []
17
 
18
 
19
  class ResourceConfig(BaseModel):
20
  resource_url: Optional[str] = None
21
- resource_urls: List[str] = [
22
  "https://raw.githubusercontent.com/MeetWq/meme-generator/",
23
  "https://ghproxy.com/https://raw.githubusercontent.com/MeetWq/meme-generator/",
24
  "https://fastly.jsdelivr.net/gh/MeetWq/meme-generator@",
 
1
  import json
2
  from pathlib import Path
3
+ from typing import Optional, Union
4
 
5
  import toml
6
  from pydantic import BaseModel, Extra
 
12
 
13
  class MemeConfig(BaseModel):
14
  load_builtin_memes: bool = True
15
+ meme_dirs: list[Path] = []
16
+ meme_disabled_list: list[str] = []
17
 
18
 
19
  class ResourceConfig(BaseModel):
20
  resource_url: Optional[str] = None
21
+ resource_urls: list[str] = [
22
  "https://raw.githubusercontent.com/MeetWq/meme-generator/",
23
  "https://ghproxy.com/https://raw.githubusercontent.com/MeetWq/meme-generator/",
24
  "https://fastly.jsdelivr.net/gh/MeetWq/meme-generator@",
meme_generator/dirs.py CHANGED
@@ -23,7 +23,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
  SOFTWARE.
24
  """
25
 
26
-
27
  import os
28
  import sys
29
  from pathlib import Path
@@ -119,7 +118,7 @@ def user_config_dir(appname: str, roaming: bool = True) -> Path:
119
 
120
  # -- Windows support functions --
121
  def _get_win_folder_from_registry(
122
- csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"]
123
  ) -> Path:
124
  """
125
  This is a fallback technique at best. I'm not sure if using the
@@ -143,7 +142,7 @@ def _get_win_folder_from_registry(
143
 
144
 
145
  def _get_win_folder_with_ctypes(
146
- csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"]
147
  ) -> Path:
148
  csidl_const = {
149
  "CSIDL_APPDATA": 26,
 
23
  SOFTWARE.
24
  """
25
 
 
26
  import os
27
  import sys
28
  from pathlib import Path
 
118
 
119
  # -- Windows support functions --
120
  def _get_win_folder_from_registry(
121
+ csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"],
122
  ) -> Path:
123
  """
124
  This is a fallback technique at best. I'm not sure if using the
 
142
 
143
 
144
  def _get_win_folder_with_ctypes(
145
+ csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"],
146
  ) -> Path:
147
  csidl_const = {
148
  "CSIDL_APPDATA": 26,
meme_generator/download.py CHANGED
@@ -3,7 +3,6 @@ import hashlib
3
  import json
4
  import time
5
  from pathlib import Path
6
- from typing import List, Tuple
7
 
8
  import httpx
9
  from rich.progress import Progress
@@ -18,13 +17,13 @@ def _resource_url(base_url: str, name: str) -> str:
18
 
19
 
20
  # https://github.com/mnixry/nonebot-plugin-gocqhttp/blob/main/nonebot_plugin_gocqhttp/process/download.py
21
- async def get_fastest_mirror() -> List[str]:
22
  assert meme_config.resource.resource_urls, "No resource url specified."
23
 
24
  async def head_mirror(client: httpx.AsyncClient, base_url: str):
25
  begin_time = time.time()
26
  response = await client.head(
27
- _resource_url(base_url, "resources/fonts/NotoSansSC-Regular.otf"), timeout=5
28
  )
29
  response.raise_for_status()
30
  elapsed_time = (time.time() - begin_time) * 1000
@@ -39,7 +38,7 @@ async def get_fastest_mirror() -> List[str]:
39
  return_exceptions=True,
40
  )
41
  results = sorted(
42
- (result for result in results if not isinstance(result, Exception)),
43
  key=lambda r: r["elapsed_time"],
44
  )
45
  return [result["base_url"] for result in results]
@@ -76,7 +75,7 @@ async def check_resources():
76
  else:
77
  return
78
 
79
- download_list: List[Tuple[Path, str]] = []
80
  for resource in resource_list:
81
  file_name = str(resource["path"])
82
  file_hash = str(resource["hash"])
 
3
  import json
4
  import time
5
  from pathlib import Path
 
6
 
7
  import httpx
8
  from rich.progress import Progress
 
17
 
18
 
19
  # https://github.com/mnixry/nonebot-plugin-gocqhttp/blob/main/nonebot_plugin_gocqhttp/process/download.py
20
+ async def get_fastest_mirror() -> list[str]:
21
  assert meme_config.resource.resource_urls, "No resource url specified."
22
 
23
  async def head_mirror(client: httpx.AsyncClient, base_url: str):
24
  begin_time = time.time()
25
  response = await client.head(
26
+ _resource_url(base_url, "resources/fonts/NotoSansSC-Regular.ttf"), timeout=5
27
  )
28
  response.raise_for_status()
29
  elapsed_time = (time.time() - begin_time) * 1000
 
38
  return_exceptions=True,
39
  )
40
  results = sorted(
41
+ (result for result in results if not isinstance(result, BaseException)),
42
  key=lambda r: r["elapsed_time"],
43
  )
44
  return [result["base_url"] for result in results]
 
75
  else:
76
  return
77
 
78
+ download_list: list[tuple[Path, str]] = []
79
  for resource in resource_list:
80
  file_name = str(resource["path"])
81
  file_hash = str(resource["hash"])
meme_generator/manager.py CHANGED
@@ -2,14 +2,14 @@ import importlib
2
  import importlib.util
3
  import pkgutil
4
  from pathlib import Path
5
- from typing import Dict, List, Optional, Union
6
 
7
  from .config import meme_config
8
  from .exception import NoSuchMeme
9
  from .log import logger
10
  from .meme import Meme, MemeArgsType, MemeFunction, MemeParamsType
11
 
12
- _memes: Dict[str, Meme] = {}
13
 
14
 
15
  def path_to_module_name(path: Path) -> str:
@@ -64,10 +64,10 @@ def add_meme(
64
  max_images: int = 0,
65
  min_texts: int = 0,
66
  max_texts: int = 0,
67
- default_texts: List[str] = [],
68
  args_type: Optional[MemeArgsType] = None,
69
- keywords: List[str] = [],
70
- patterns: List[str] = [],
71
  ):
72
  if key in _memes:
73
  logger.warning(f'Meme with key "{key}" already exists!')
@@ -96,9 +96,9 @@ def get_meme(key: str) -> Meme:
96
  return _memes[key]
97
 
98
 
99
- def get_memes() -> List[Meme]:
100
  return list(_memes.values())
101
 
102
 
103
- def get_meme_keys() -> List[str]:
104
  return list(_memes.keys())
 
2
  import importlib.util
3
  import pkgutil
4
  from pathlib import Path
5
+ from typing import Optional, Union
6
 
7
  from .config import meme_config
8
  from .exception import NoSuchMeme
9
  from .log import logger
10
  from .meme import Meme, MemeArgsType, MemeFunction, MemeParamsType
11
 
12
+ _memes: dict[str, Meme] = {}
13
 
14
 
15
  def path_to_module_name(path: Path) -> str:
 
64
  max_images: int = 0,
65
  min_texts: int = 0,
66
  max_texts: int = 0,
67
+ default_texts: list[str] = [],
68
  args_type: Optional[MemeArgsType] = None,
69
+ keywords: list[str] = [],
70
+ patterns: list[str] = [],
71
  ):
72
  if key in _memes:
73
  logger.warning(f'Meme with key "{key}" already exists!')
 
96
  return _memes[key]
97
 
98
 
99
+ def get_memes() -> list[Meme]:
100
  return list(_memes.values())
101
 
102
 
103
+ def get_meme_keys() -> list[str]:
104
  return list(_memes.keys())
meme_generator/meme.py CHANGED
@@ -1,5 +1,6 @@
1
  import copy
2
  from argparse import ArgumentError, ArgumentParser
 
3
  from contextvars import ContextVar
4
  from dataclasses import dataclass, field
5
  from io import BytesIO
@@ -7,13 +8,9 @@ from pathlib import Path
7
  from typing import (
8
  IO,
9
  Any,
10
- Awaitable,
11
  Callable,
12
- Dict,
13
- List,
14
  Literal,
15
  Optional,
16
- Type,
17
  TypeVar,
18
  Union,
19
  cast,
@@ -40,14 +37,14 @@ class UserInfo(BaseModel):
40
 
41
 
42
  class MemeArgsModel(BaseModel):
43
- user_infos: List[UserInfo] = []
44
 
45
 
46
  ArgsModel = TypeVar("ArgsModel", bound=MemeArgsModel)
47
 
48
  MemeFunction = Union[
49
- Callable[[List[BuildImage], List[str], ArgsModel], BytesIO],
50
- Callable[[List[BuildImage], List[str], ArgsModel], Awaitable[BytesIO]],
51
  ]
52
 
53
 
@@ -77,8 +74,8 @@ class MemeArgsParser(ArgumentParser):
77
  @dataclass
78
  class MemeArgsType:
79
  parser: MemeArgsParser
80
- model: Type[MemeArgsModel]
81
- instances: List[MemeArgsModel] = field(default_factory=list)
82
 
83
 
84
  @dataclass
@@ -87,7 +84,7 @@ class MemeParamsType:
87
  max_images: int = 0
88
  min_texts: int = 0
89
  max_texts: int = 0
90
- default_texts: List[str] = field(default_factory=list)
91
  args_type: Optional[MemeArgsType] = None
92
 
93
 
@@ -96,15 +93,15 @@ class Meme:
96
  key: str
97
  function: MemeFunction
98
  params_type: MemeParamsType
99
- keywords: List[str] = field(default_factory=list)
100
- patterns: List[str] = field(default_factory=list)
101
 
102
  async def __call__(
103
  self,
104
  *,
105
- images: Union[List[str], List[Path], List[bytes], List[BytesIO]] = [],
106
- texts: List[str] = [],
107
- args: Dict[str, Any] = {},
108
  ) -> BytesIO:
109
  if not (
110
  self.params_type.min_images <= len(images) <= self.params_type.max_images
@@ -128,12 +125,12 @@ class Meme:
128
  except ValidationError as e:
129
  raise ArgModelMismatch(self.key, str(e))
130
 
131
- imgs: List[BuildImage] = []
132
  try:
133
  for image in images:
134
  if isinstance(image, bytes):
135
  image = BytesIO(image)
136
- imgs.append(BuildImage.open(image))
137
  except Exception as e:
138
  raise OpenImageFailed(str(e))
139
 
@@ -146,7 +143,7 @@ class Meme:
146
  else:
147
  return await run_sync(cast(Callable[..., BytesIO], self.function))(**values)
148
 
149
- def parse_args(self, args: List[str] = []) -> Dict[str, Any]:
150
  parser = (
151
  copy.deepcopy(self.params_type.args_type.parser)
152
  if self.params_type.args_type
@@ -163,7 +160,7 @@ class Meme:
163
  finally:
164
  parser_message.reset(t)
165
 
166
- async def generate_preview(self, *, args: Dict[str, Any] = {}) -> BytesIO:
167
  default_images = [random_image() for _ in range(self.params_type.min_images)]
168
  default_texts = (
169
  self.params_type.default_texts.copy()
@@ -175,7 +172,7 @@ class Meme:
175
  else [random_text() for _ in range(self.params_type.min_texts)]
176
  )
177
 
178
- async def _generate_preview(images: List[BytesIO], texts: List[str]):
179
  try:
180
  return await self.__call__(images=images, texts=texts, args=args)
181
  except TextOrNameNotEnough:
 
1
  import copy
2
  from argparse import ArgumentError, ArgumentParser
3
+ from collections.abc import Awaitable
4
  from contextvars import ContextVar
5
  from dataclasses import dataclass, field
6
  from io import BytesIO
 
8
  from typing import (
9
  IO,
10
  Any,
 
11
  Callable,
 
 
12
  Literal,
13
  Optional,
 
14
  TypeVar,
15
  Union,
16
  cast,
 
37
 
38
 
39
  class MemeArgsModel(BaseModel):
40
+ user_infos: list[UserInfo] = []
41
 
42
 
43
  ArgsModel = TypeVar("ArgsModel", bound=MemeArgsModel)
44
 
45
  MemeFunction = Union[
46
+ Callable[[list[BuildImage], list[str], ArgsModel], BytesIO],
47
+ Callable[[list[BuildImage], list[str], ArgsModel], Awaitable[BytesIO]],
48
  ]
49
 
50
 
 
74
  @dataclass
75
  class MemeArgsType:
76
  parser: MemeArgsParser
77
+ model: type[MemeArgsModel]
78
+ instances: list[MemeArgsModel] = field(default_factory=list)
79
 
80
 
81
  @dataclass
 
84
  max_images: int = 0
85
  min_texts: int = 0
86
  max_texts: int = 0
87
+ default_texts: list[str] = field(default_factory=list)
88
  args_type: Optional[MemeArgsType] = None
89
 
90
 
 
93
  key: str
94
  function: MemeFunction
95
  params_type: MemeParamsType
96
+ keywords: list[str] = field(default_factory=list)
97
+ patterns: list[str] = field(default_factory=list)
98
 
99
  async def __call__(
100
  self,
101
  *,
102
+ images: Union[list[str], list[Path], list[bytes], list[BytesIO]] = [],
103
+ texts: list[str] = [],
104
+ args: dict[str, Any] = {},
105
  ) -> BytesIO:
106
  if not (
107
  self.params_type.min_images <= len(images) <= self.params_type.max_images
 
125
  except ValidationError as e:
126
  raise ArgModelMismatch(self.key, str(e))
127
 
128
+ imgs: list[BuildImage] = []
129
  try:
130
  for image in images:
131
  if isinstance(image, bytes):
132
  image = BytesIO(image)
133
+ imgs.append(BuildImage.open(image)) # type: ignore
134
  except Exception as e:
135
  raise OpenImageFailed(str(e))
136
 
 
143
  else:
144
  return await run_sync(cast(Callable[..., BytesIO], self.function))(**values)
145
 
146
+ def parse_args(self, args: list[str] = []) -> dict[str, Any]:
147
  parser = (
148
  copy.deepcopy(self.params_type.args_type.parser)
149
  if self.params_type.args_type
 
160
  finally:
161
  parser_message.reset(t)
162
 
163
+ async def generate_preview(self, *, args: dict[str, Any] = {}) -> BytesIO:
164
  default_images = [random_image() for _ in range(self.params_type.min_images)]
165
  default_texts = (
166
  self.params_type.default_texts.copy()
 
172
  else [random_text() for _ in range(self.params_type.min_texts)]
173
  )
174
 
175
+ async def _generate_preview(images: list[BytesIO], texts: list[str]):
176
  try:
177
  return await self.__call__(images=images, texts=texts, args=args)
178
  except TextOrNameNotEnough:
meme_generator/memes/5000choyen/__init__.py CHANGED
@@ -1,5 +1,3 @@
1
- from typing import List, Tuple
2
-
3
  from PIL.Image import Image as IMG
4
  from PIL.Image import Resampling, Transform
5
  from pil_utils import BuildImage, Text2Image
@@ -8,13 +6,13 @@ from pil_utils.gradient import ColorStop, LinearGradient
8
  from meme_generator import add_meme
9
 
10
 
11
- def fivethousand_choyen(images, texts: List[str], args):
12
  fontsize = 200
13
  fontname = "Noto Sans SC"
14
  text = texts[0]
15
  pos_x = 40
16
  pos_y = 220
17
- imgs: List[Tuple[IMG, Tuple[int, int]]] = []
18
 
19
  def transform(img: IMG) -> IMG:
20
  skew = 0.45
@@ -26,7 +24,7 @@ def fivethousand_choyen(images, texts: List[str], args):
26
  Resampling.BILINEAR,
27
  )
28
 
29
- def shift(t2m: Text2Image) -> Tuple[int, int]:
30
  return (
31
  pos_x
32
  - t2m.lines[0].chars[0].stroke_width
@@ -34,7 +32,7 @@ def fivethousand_choyen(images, texts: List[str], args):
34
  pos_y - t2m.lines[0].ascent,
35
  )
36
 
37
- def add_color_text(stroke_width: int, fill: str, pos: Tuple[int, int]):
38
  t2m = Text2Image.from_text(
39
  text, fontsize, fontname=fontname, stroke_width=stroke_width, fill=fill
40
  )
@@ -43,9 +41,9 @@ def fivethousand_choyen(images, texts: List[str], args):
43
 
44
  def add_gradient_text(
45
  stroke_width: int,
46
- dir: Tuple[int, int, int, int],
47
- color_stops: List[Tuple[float, Tuple[int, int, int]]],
48
- pos: Tuple[int, int],
49
  ):
50
  t2m = Text2Image.from_text(
51
  text, fontsize, fontname=fontname, stroke_width=stroke_width, fill="white"
 
 
 
1
  from PIL.Image import Image as IMG
2
  from PIL.Image import Resampling, Transform
3
  from pil_utils import BuildImage, Text2Image
 
6
  from meme_generator import add_meme
7
 
8
 
9
+ def fivethousand_choyen(images, texts: list[str], args):
10
  fontsize = 200
11
  fontname = "Noto Sans SC"
12
  text = texts[0]
13
  pos_x = 40
14
  pos_y = 220
15
+ imgs: list[tuple[IMG, tuple[int, int]]] = []
16
 
17
  def transform(img: IMG) -> IMG:
18
  skew = 0.45
 
24
  Resampling.BILINEAR,
25
  )
26
 
27
+ def shift(t2m: Text2Image) -> tuple[int, int]:
28
  return (
29
  pos_x
30
  - t2m.lines[0].chars[0].stroke_width
 
32
  pos_y - t2m.lines[0].ascent,
33
  )
34
 
35
+ def add_color_text(stroke_width: int, fill: str, pos: tuple[int, int]):
36
  t2m = Text2Image.from_text(
37
  text, fontsize, fontname=fontname, stroke_width=stroke_width, fill=fill
38
  )
 
41
 
42
  def add_gradient_text(
43
  stroke_width: int,
44
+ dir: tuple[int, int, int, int],
45
+ color_stops: list[tuple[float, tuple[int, int, int]]],
46
+ pos: tuple[int, int],
47
  ):
48
  t2m = Text2Image.from_text(
49
  text, fontsize, fontname=fontname, stroke_width=stroke_width, fill="white"
meme_generator/memes/ace_attorney_dialog/__init__.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from pathlib import Path
3
+
4
+ from pil_utils import BuildImage, Text2Image
5
+
6
+ from meme_generator import add_meme
7
+ from meme_generator.exception import TextOverLength
8
+
9
+ img_dir = Path(__file__).parent / "images"
10
+
11
+
12
+ def ace_attorney_dialog(images, texts: list[str], args):
13
+ def shadow_text(text: str, fontsize: int) -> BuildImage:
14
+ fontname = "PangMenZhengDao-Cu"
15
+ inner = Text2Image.from_text(
16
+ text,
17
+ fontsize,
18
+ fill="#e60012",
19
+ fontname=fontname,
20
+ stroke_width=4,
21
+ stroke_fill="#500000",
22
+ ).to_image()
23
+ shadow_width = 10
24
+ shadow = Text2Image.from_text(
25
+ text,
26
+ fontsize,
27
+ fill="#500000",
28
+ fontname=fontname,
29
+ stroke_width=shadow_width,
30
+ stroke_fill="#500000",
31
+ ).to_image()
32
+ dy = 30
33
+ dx = 15
34
+ img = BuildImage.new(
35
+ "RGBA", (inner.width + dx + shadow_width, inner.height + dy + shadow_width)
36
+ )
37
+ img.paste(shadow, (dx - shadow_width, dy - shadow_width), alpha=True)
38
+ img.paste(inner, (0, 0), alpha=True)
39
+ return img
40
+
41
+ text = texts[0]
42
+ text_imgs: list[BuildImage] = []
43
+ for char in text:
44
+ text_imgs.append(shadow_text(char, 650))
45
+
46
+ total_width = sum(img.width for img in text_imgs)
47
+ if total_width > 4000:
48
+ raise TextOverLength(text)
49
+
50
+ def combine_text(text_imgs: list[BuildImage]) -> BuildImage:
51
+ ratio = 0.4
52
+ text_w = sum(img.width for img in text_imgs) - sum(
53
+ round(img.width * ratio) for img in text_imgs[1:]
54
+ )
55
+ text_h = max(img.height for img in text_imgs)
56
+ text_img = BuildImage.new("RGBA", (text_w, text_h))
57
+ x = 0
58
+ for img in text_imgs:
59
+ text_img.paste(img, (x, round((text_h - img.height) / 2)), alpha=True)
60
+ x += img.width - round(img.width * ratio)
61
+ return text_img
62
+
63
+ frame = BuildImage.open(img_dir / "bubble.png")
64
+ mark = BuildImage.open(img_dir / "mark.png")
65
+
66
+ if total_width <= 2000:
67
+ text_img = combine_text(text_imgs)
68
+ max_width = 900
69
+ if total_width > max_width:
70
+ text_img = text_img.resize(
71
+ (max_width, round(max_width / text_img.width * text_img.height))
72
+ )
73
+ text_img = text_img.rotate(10, expand=True)
74
+ frame.paste(
75
+ text_img,
76
+ (
77
+ round((frame.width - text_img.width) / 2),
78
+ round((frame.height - text_img.height) / 2),
79
+ ),
80
+ alpha=True,
81
+ )
82
+ frame.paste(mark, (630, 230), alpha=True)
83
+
84
+ else:
85
+ index = math.ceil(len(text_imgs) / 2)
86
+ text_img1 = combine_text(text_imgs[:index])
87
+ text_img2 = combine_text(text_imgs[index:])
88
+ ratio = 0.6
89
+ text_img1 = text_img1.resize(
90
+ (round(text_img1.width * ratio), round(text_img1.height * ratio))
91
+ )
92
+ text_img2 = text_img2.resize(
93
+ (round(text_img2.width * ratio), round(text_img2.height * ratio))
94
+ )
95
+ text_img1 = text_img1.rotate(10, expand=True)
96
+ text_img2 = text_img2.rotate(10, expand=True)
97
+ frame.paste(
98
+ text_img1,
99
+ (round((frame.width - text_img1.width) / 2) - 50, 775 - text_img1.height),
100
+ alpha=True,
101
+ )
102
+ frame.paste(
103
+ text_img2,
104
+ (round((frame.width - text_img2.width) / 2) + 50, 325),
105
+ alpha=True,
106
+ )
107
+ frame.paste(mark, (680, 320), alpha=True)
108
+
109
+ return frame.save_png()
110
+
111
+
112
+ add_meme(
113
+ "ace_attorney_dialog",
114
+ ace_attorney_dialog,
115
+ min_texts=1,
116
+ max_texts=1,
117
+ default_texts=["表情包制作"],
118
+ keywords=["逆转裁判气泡"],
119
+ )
meme_generator/memes/ace_attorney_dialog/images/bubble.png ADDED

Git LFS Details

  • SHA256: 9c762c91e86cddc92b04712d1c6d0c7156c55a7b81921df819c5c447418e6764
  • Pointer size: 130 Bytes
  • Size of remote file: 62 kB
meme_generator/memes/ace_attorney_dialog/images/mark.png ADDED

Git LFS Details

  • SHA256: 7b5880eff0ef9441f13e4b4f4587ba14323fac4b6631ac946d9934b33e2e5edc
  • Pointer size: 130 Bytes
  • Size of remote file: 23 kB
meme_generator/memes/acg_entrance/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -10,7 +9,7 @@ from meme_generator.utils import make_jpg_or_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def acg_entrance(images: List[BuildImage], texts: List[str], args):
14
  text = texts[0] if texts else "走,跟我去二次元吧"
15
  frame = BuildImage.open(img_dir / "0.png")
16
  try:
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def acg_entrance(images: list[BuildImage], texts: list[str], args):
13
  text = texts[0] if texts else "走,跟我去二次元吧"
14
  frame = BuildImage.open(img_dir / "0.png")
15
  try:
meme_generator/memes/add_chaos/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -9,7 +8,7 @@ from meme_generator.utils import make_jpg_or_gif
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
- def add_chaos(images: List[BuildImage], texts, args):
13
  banner = BuildImage.open(img_dir / "0.png")
14
 
15
  def make(img: BuildImage) -> BuildImage:
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
+ def add_chaos(images: list[BuildImage], texts, args):
12
  banner = BuildImage.open(img_dir / "0.png")
13
 
14
  def make(img: BuildImage) -> BuildImage:
meme_generator/memes/addiction/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -10,7 +9,7 @@ from meme_generator.utils import make_jpg_or_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def addiction(images: List[BuildImage], texts: List[str], args):
14
  frame = BuildImage.open(img_dir / "0.png")
15
 
16
  if texts:
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def addiction(images: list[BuildImage], texts: list[str], args):
13
  frame = BuildImage.open(img_dir / "0.png")
14
 
15
  if texts:
meme_generator/memes/alike/__init__.py CHANGED
@@ -1,12 +1,10 @@
1
- from typing import List
2
-
3
  from pil_utils import BuildImage
4
 
5
  from meme_generator import add_meme
6
  from meme_generator.utils import make_jpg_or_gif
7
 
8
 
9
- def alike(images: List[BuildImage], texts, args):
10
  frame = BuildImage.new("RGBA", (470, 180), "white")
11
  frame.draw_text(
12
  (10, 10, 185, 140), "你怎么跟", max_fontsize=40, min_fontsize=30, halign="right"
 
 
 
1
  from pil_utils import BuildImage
2
 
3
  from meme_generator import add_meme
4
  from meme_generator.utils import make_jpg_or_gif
5
 
6
 
7
+ def alike(images: list[BuildImage], texts, args):
8
  frame = BuildImage.new("RGBA", (470, 180), "white")
9
  frame.draw_text(
10
  (10, 10, 185, 140), "你怎么跟", max_fontsize=40, min_fontsize=30, halign="right"
meme_generator/memes/always/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import List, Literal
2
 
3
  from pil_utils import BuildImage
4
  from pydantic import Field
@@ -22,7 +22,9 @@ group.add_argument(
22
  default="normal",
23
  help=help,
24
  )
25
- group.add_argument("--circle", "/套娃", action="store_const", const="circle", dest="mode")
 
 
26
  group.add_argument("--loop", "/循环", action="store_const", const="loop", dest="mode")
27
 
28
 
@@ -90,7 +92,7 @@ def always_always(img: BuildImage, loop: bool = False):
90
  )
91
 
92
 
93
- def always(images: List[BuildImage], texts, args: Model):
94
  img = images[0]
95
  mode = args.mode
96
 
 
1
+ from typing import Literal
2
 
3
  from pil_utils import BuildImage
4
  from pydantic import Field
 
22
  default="normal",
23
  help=help,
24
  )
25
+ group.add_argument(
26
+ "--circle", "/套娃", action="store_const", const="circle", dest="mode"
27
+ )
28
  group.add_argument("--loop", "/循环", action="store_const", const="loop", dest="mode")
29
 
30
 
 
92
  )
93
 
94
 
95
+ def always(images: list[BuildImage], texts, args: Model):
96
  img = images[0]
97
  mode = args.mode
98
 
meme_generator/memes/always_like/__init__.py CHANGED
@@ -1,6 +1,5 @@
1
  import random
2
  from pathlib import Path
3
- from typing import List
4
 
5
  from pil_utils import BuildImage, Text2Image
6
 
@@ -10,7 +9,7 @@ from meme_generator.exception import TextOrNameNotEnough, TextOverLength
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def always_like(images: List[BuildImage], texts: List[str], args: MemeArgsModel):
14
  names = [info.name for info in args.user_infos]
15
 
16
  if len(images) > len(texts) + len(names):
 
1
  import random
2
  from pathlib import Path
 
3
 
4
  from pil_utils import BuildImage, Text2Image
5
 
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def always_like(images: list[BuildImage], texts: list[str], args: MemeArgsModel):
13
  names = [info.name for info in args.user_infos]
14
 
15
  if len(images) > len(texts) + len(names):
meme_generator/memes/anti_kidnap/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -8,7 +7,7 @@ from meme_generator import add_meme
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
- def anti_kidnap(images: List[BuildImage], texts, args):
12
  img = images[0].convert("RGBA").resize((450, 450), keep_ratio=True)
13
  frame = BuildImage.open(img_dir / "0.png")
14
  frame.paste(img, (30, 78), below=True)
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
7
  img_dir = Path(__file__).parent / "images"
8
 
9
 
10
+ def anti_kidnap(images: list[BuildImage], texts, args):
11
  img = images[0].convert("RGBA").resize((450, 450), keep_ratio=True)
12
  frame = BuildImage.open(img_dir / "0.png")
13
  frame.paste(img, (30, 78), below=True)
meme_generator/memes/anya_suki/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -10,7 +9,7 @@ from meme_generator.utils import make_jpg_or_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def anya_suki(images: List[BuildImage], texts: List[str], args):
14
  text = texts[0] if texts else "阿尼亚喜欢这个"
15
  frame = BuildImage.open(img_dir / "0.png")
16
  try:
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def anya_suki(images: list[BuildImage], texts: list[str], args):
13
  text = texts[0] if texts else "阿尼亚喜欢这个"
14
  frame = BuildImage.open(img_dir / "0.png")
15
  try:
meme_generator/memes/applaud/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -10,9 +9,9 @@ from meme_generator.utils import save_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def applaud(images: List[BuildImage], texts, args):
14
  img = images[0].convert("RGBA").square().resize((110, 110))
15
- frames: List[IMG] = []
16
  locs = [
17
  (109, 102, 27, 17),
18
  (107, 105, 28, 15),
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def applaud(images: list[BuildImage], texts, args):
13
  img = images[0].convert("RGBA").square().resize((110, 110))
14
+ frames: list[IMG] = []
15
  locs = [
16
  (109, 102, 27, 17),
17
  (107, 105, 28, 15),
meme_generator/memes/ascension/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -9,7 +8,7 @@ from meme_generator.exception import TextOverLength
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
- def ascension(images, texts: List[str], args):
13
  frame = BuildImage.open(img_dir / "0.png")
14
  text = f"你原本应该要去地狱的,但因为你生前{texts[0]},我们就当作你已经服完刑期了"
15
  try:
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
+ def ascension(images, texts: list[str], args):
12
  frame = BuildImage.open(img_dir / "0.png")
13
  text = f"你原本应该要去地狱的,但因为你生前{texts[0]},我们就当作你已经服完刑期了"
14
  try:
meme_generator/memes/ask/__init__.py CHANGED
@@ -1,5 +1,3 @@
1
- from typing import List
2
-
3
  from PIL import ImageFilter
4
  from pil_utils import BuildImage, Text2Image
5
  from pil_utils.gradient import ColorStop, LinearGradient
@@ -8,7 +6,7 @@ from meme_generator import MemeArgsModel, add_meme
8
  from meme_generator.exception import TextOrNameNotEnough, TextOverLength
9
 
10
 
11
- def ask(images: List[BuildImage], texts: List[str], args: MemeArgsModel):
12
  if not texts and not args.user_infos:
13
  raise TextOrNameNotEnough("ask")
14
 
 
 
 
1
  from PIL import ImageFilter
2
  from pil_utils import BuildImage, Text2Image
3
  from pil_utils.gradient import ColorStop, LinearGradient
 
6
  from meme_generator.exception import TextOrNameNotEnough, TextOverLength
7
 
8
 
9
+ def ask(images: list[BuildImage], texts: list[str], args: MemeArgsModel):
10
  if not texts and not args.user_infos:
11
  raise TextOrNameNotEnough("ask")
12
 
meme_generator/memes/back_to_work/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -8,7 +7,7 @@ from meme_generator import add_meme
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
- def back_to_work(images: List[BuildImage], texts, args):
12
  frame = BuildImage.open(img_dir / "0.png")
13
  img = (
14
  images[0].convert("RGBA").resize((220, 310), keep_ratio=True, direction="north")
@@ -18,5 +17,9 @@ def back_to_work(images: List[BuildImage], texts, args):
18
 
19
 
20
  add_meme(
21
- "back_to_work", back_to_work, min_images=1, max_images=1, keywords=["继续干活", "打工人"]
 
 
 
 
22
  )
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
7
  img_dir = Path(__file__).parent / "images"
8
 
9
 
10
+ def back_to_work(images: list[BuildImage], texts, args):
11
  frame = BuildImage.open(img_dir / "0.png")
12
  img = (
13
  images[0].convert("RGBA").resize((220, 310), keep_ratio=True, direction="north")
 
17
 
18
 
19
  add_meme(
20
+ "back_to_work",
21
+ back_to_work,
22
+ min_images=1,
23
+ max_images=1,
24
+ keywords=["继续干活", "打工人"],
25
  )
meme_generator/memes/bad_news/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -9,7 +8,7 @@ from meme_generator.exception import TextOverLength
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
- def bad_news(images, texts: List[str], args):
13
  text = texts[0]
14
  frame = BuildImage.open(img_dir / "0.png")
15
  try:
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
+ def bad_news(images, texts: list[str], args):
12
  text = texts[0]
13
  frame = BuildImage.open(img_dir / "0.png")
14
  try:
meme_generator/memes/beat_head/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -11,11 +10,11 @@ from meme_generator.utils import save_gif
11
  img_dir = Path(__file__).parent / "images"
12
 
13
 
14
- def beat_head(images: List[BuildImage], texts: List[str], args):
15
  text = texts[0] if texts else "怎么说话的你"
16
  img = images[0].convert("RGBA")
17
  locs = [(160, 121, 76, 76), (172, 124, 69, 69), (208, 166, 52, 52)]
18
- frames: List[IMG] = []
19
  for i in range(3):
20
  x, y, w, h = locs[i]
21
  head = img.resize((w, h), keep_ratio=True).circle()
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
+ def beat_head(images: list[BuildImage], texts: list[str], args):
14
  text = texts[0] if texts else "怎么说话的你"
15
  img = images[0].convert("RGBA")
16
  locs = [(160, 121, 76, 76), (172, 124, 69, 69), (208, 166, 52, 52)]
17
+ frames: list[IMG] = []
18
  for i in range(3):
19
  x, y, w, h = locs[i]
20
  head = img.resize((w, h), keep_ratio=True).circle()
meme_generator/memes/beat_up/__init__.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ from PIL.Image import Image as IMG
4
+ from pil_utils import BuildImage
5
+
6
+ from meme_generator import add_meme
7
+ from meme_generator.utils import save_gif
8
+
9
+ img_dir = Path(__file__).parent / "images"
10
+
11
+
12
+ def beat_up(images: list[BuildImage], texts, args):
13
+ self_head = images[0].convert("RGBA").circle().resize((55, 55))
14
+ user_head = images[1].convert("RGBA").circle().resize((45, 45))
15
+ self_locs = [(100, 43), (110, 46), (101, 40)]
16
+ user_locs = [(99, 136), (99, 136), (89, 140)]
17
+ frames: list[IMG] = []
18
+ for i in range(3):
19
+ frame = BuildImage.open(img_dir / f"{i}.png")
20
+ frame.paste(user_head, user_locs[i], alpha=True)
21
+ frame.paste(self_head, self_locs[i], alpha=True)
22
+ frames.append(frame.image)
23
+
24
+ return save_gif(frames, 0.1)
25
+
26
+
27
+ add_meme("beat_up", beat_up, min_images=2, max_images=2, keywords=["揍"])
meme_generator/memes/beat_up/images/0.png ADDED

Git LFS Details

  • SHA256: a60a42f56fcab0fff10d805716a05e4d5819b988975e5553c3a5b1a12ed6662f
  • Pointer size: 130 Bytes
  • Size of remote file: 81.3 kB
meme_generator/memes/beat_up/images/1.png ADDED

Git LFS Details

  • SHA256: 7aa42456a87f32d1033ea5d06634024e4dde96139a56eb8e383ff58ead97963c
  • Pointer size: 130 Bytes
  • Size of remote file: 85.3 kB
meme_generator/memes/beat_up/images/2.png ADDED

Git LFS Details

  • SHA256: d0c4704293099ef958d5320ea5831365ac186eebc9bd865e6d5a47910e294a94
  • Pointer size: 130 Bytes
  • Size of remote file: 83.6 kB
meme_generator/memes/bite/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -10,9 +9,9 @@ from meme_generator.utils import save_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def bite(images: List[BuildImage], texts, args):
14
  img = images[0].convert("RGBA").square()
15
- frames: List[IMG] = []
16
  # fmt: off
17
  locs = [
18
  (90, 90, 105, 150), (90, 83, 96, 172), (90, 90, 106, 148),
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def bite(images: list[BuildImage], texts, args):
13
  img = images[0].convert("RGBA").square()
14
+ frames: list[IMG] = []
15
  # fmt: off
16
  locs = [
17
  (90, 90, 105, 150), (90, 83, 96, 172), (90, 90, 106, 148),
meme_generator/memes/blood_pressure/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -9,7 +8,7 @@ from meme_generator.utils import make_jpg_or_gif
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
- def blood_pressure(images: List[BuildImage], texts, args):
13
  frame = BuildImage.open(img_dir / "0.png")
14
 
15
  def make(img: BuildImage) -> BuildImage:
@@ -19,4 +18,6 @@ def blood_pressure(images: List[BuildImage], texts, args):
19
  return make_jpg_or_gif(images[0], make)
20
 
21
 
22
- add_meme("blood_pressure", blood_pressure, min_images=1, max_images=1, keywords=["高血压"])
 
 
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
+ def blood_pressure(images: list[BuildImage], texts, args):
12
  frame = BuildImage.open(img_dir / "0.png")
13
 
14
  def make(img: BuildImage) -> BuildImage:
 
18
  return make_jpg_or_gif(images[0], make)
19
 
20
 
21
+ add_meme(
22
+ "blood_pressure", blood_pressure, min_images=1, max_images=1, keywords=["高血压"]
23
+ )
meme_generator/memes/bluearchive/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from PIL.Image import Resampling, Transform
@@ -12,7 +11,7 @@ from meme_generator import add_meme
12
  img_dir = Path(__file__).parent / "images"
13
 
14
 
15
- def bluearchive(images, texts: List[str], args):
16
  fontsize = 168
17
  fontname = "Ro GSan Serif Std"
18
  fallback_fonts = ["Glow Sans SC"] + DEFAULT_FALLBACK_FONTS
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from PIL.Image import Resampling, Transform
 
11
  img_dir = Path(__file__).parent / "images"
12
 
13
 
14
+ def bluearchive(images, texts: list[str], args):
15
  fontsize = 168
16
  fontname = "Ro GSan Serif Std"
17
  fallback_fonts = ["Glow Sans SC"] + DEFAULT_FALLBACK_FONTS
meme_generator/memes/bocchi_draft/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -10,7 +9,7 @@ from meme_generator.utils import save_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def bocchi_draft(images: List[BuildImage], texts, args):
14
  img = images[0].convert("RGBA").resize((350, 400), keep_ratio=True)
15
  params = [
16
  (((54, 62), (353, 1), (379, 382), (1, 399)), (146, 173)),
@@ -30,7 +29,7 @@ def bocchi_draft(images: List[BuildImage], texts, args):
30
  0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10,
31
  ]
32
  # fmt: on
33
- frames: List[IMG] = []
34
  for i in range(23):
35
  frame = BuildImage.open(img_dir / f"{i}.png")
36
  points, pos = params[idx[i]]
@@ -39,4 +38,6 @@ def bocchi_draft(images: List[BuildImage], texts, args):
39
  return save_gif(frames, 0.08)
40
 
41
 
42
- add_meme("bocchi_draft", bocchi_draft, min_images=1, max_images=1, keywords=["波奇手稿"])
 
 
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def bocchi_draft(images: list[BuildImage], texts, args):
13
  img = images[0].convert("RGBA").resize((350, 400), keep_ratio=True)
14
  params = [
15
  (((54, 62), (353, 1), (379, 382), (1, 399)), (146, 173)),
 
29
  0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10,
30
  ]
31
  # fmt: on
32
+ frames: list[IMG] = []
33
  for i in range(23):
34
  frame = BuildImage.open(img_dir / f"{i}.png")
35
  points, pos = params[idx[i]]
 
38
  return save_gif(frames, 0.08)
39
 
40
 
41
+ add_meme(
42
+ "bocchi_draft", bocchi_draft, min_images=1, max_images=1, keywords=["波奇手稿"]
43
+ )
meme_generator/memes/bronya_holdsign/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -9,7 +8,7 @@ from meme_generator.exception import TextOverLength
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
- def bronya_holdsign(images, texts: List[str], args):
13
  text = texts[0]
14
  frame = BuildImage.open(img_dir / "0.jpg")
15
  try:
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
+ def bronya_holdsign(images, texts: list[str], args):
12
  text = texts[0]
13
  frame = BuildImage.open(img_dir / "0.jpg")
14
  try:
meme_generator/memes/bubble_tea/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from pathlib import Path
2
- from typing import List, Literal
3
 
4
  from PIL.Image import Transpose
5
  from pil_utils import BuildImage
@@ -26,15 +26,19 @@ group.add_argument(
26
  group.add_argument(
27
  "--right", "/右手", action="store_const", const="right", dest="position"
28
  )
29
- group.add_argument("--left", "/左手", action="store_const", const="left", dest="position")
30
- group.add_argument("--both", "/双手", action="store_const", const="both", dest="position")
 
 
 
 
31
 
32
 
33
  class Model(MemeArgsModel):
34
  position: Literal["right", "left", "both"] = Field("right", description=help)
35
 
36
 
37
- def bubble_tea(images: List[BuildImage], texts, args: Model):
38
  frame = images[0].convert("RGBA").resize((500, 500), keep_ratio=True)
39
  bubble_tea = BuildImage.open(img_dir / "0.png")
40
  position = args.position
 
1
  from pathlib import Path
2
+ from typing import Literal
3
 
4
  from PIL.Image import Transpose
5
  from pil_utils import BuildImage
 
26
  group.add_argument(
27
  "--right", "/右手", action="store_const", const="right", dest="position"
28
  )
29
+ group.add_argument(
30
+ "--left", "/左手", action="store_const", const="left", dest="position"
31
+ )
32
+ group.add_argument(
33
+ "--both", "/双手", action="store_const", const="both", dest="position"
34
+ )
35
 
36
 
37
  class Model(MemeArgsModel):
38
  position: Literal["right", "left", "both"] = Field("right", description=help)
39
 
40
 
41
+ def bubble_tea(images: list[BuildImage], texts, args: Model):
42
  frame = images[0].convert("RGBA").resize((500, 500), keep_ratio=True)
43
  bubble_tea = BuildImage.open(img_dir / "0.png")
44
  position = args.position
meme_generator/memes/call_110/__init__.py CHANGED
@@ -1,11 +1,9 @@
1
- from typing import List
2
-
3
  from pil_utils import BuildImage
4
 
5
  from meme_generator import add_meme
6
 
7
 
8
- def call_110(images: List[BuildImage], texts, args):
9
  img1 = images[0].convert("RGBA").square().resize((250, 250))
10
  img0 = images[1].convert("RGBA").square().resize((250, 250))
11
 
 
 
 
1
  from pil_utils import BuildImage
2
 
3
  from meme_generator import add_meme
4
 
5
 
6
+ def call_110(images: list[BuildImage], texts, args):
7
  img1 = images[0].convert("RGBA").square().resize((250, 250))
8
  img0 = images[1].convert("RGBA").square().resize((250, 250))
9
 
meme_generator/memes/caoshen_bite/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -10,7 +9,7 @@ from meme_generator.utils import save_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def caoshen_bite(images: List[BuildImage], texts, args):
14
  img = images[0].convert("RGBA").resize((160, 140), keep_ratio=True)
15
  # fmt: off
16
  locs = [
@@ -22,7 +21,7 @@ def caoshen_bite(images: List[BuildImage], texts, args):
22
  (122, 351, 159, 129), (122, 353, 159, 127), (123, 355, 158, 125),
23
  ]
24
  # fmt: on
25
- frames: List[IMG] = []
26
  for i in range(38):
27
  frame = BuildImage.open(img_dir / f"{i}.png")
28
  x, y, w, h = locs[i % len(locs)]
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def caoshen_bite(images: list[BuildImage], texts, args):
13
  img = images[0].convert("RGBA").resize((160, 140), keep_ratio=True)
14
  # fmt: off
15
  locs = [
 
21
  (122, 351, 159, 129), (122, 353, 159, 127), (123, 355, 158, 125),
22
  ]
23
  # fmt: on
24
+ frames: list[IMG] = []
25
  for i in range(38):
26
  frame = BuildImage.open(img_dir / f"{i}.png")
27
  x, y, w, h = locs[i % len(locs)]
meme_generator/memes/capoo_draw/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -10,7 +9,7 @@ from meme_generator.utils import save_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def capoo_draw(images: List[BuildImage], texts, args):
14
  img = images[0].convert("RGBA").resize((175, 120), keep_ratio=True)
15
  params = (
16
  (((27, 0), (207, 12), (179, 142), (0, 117)), (30, 16)),
@@ -21,7 +20,7 @@ def capoo_draw(images: List[BuildImage], texts, args):
21
  points, pos = params[i]
22
  raw_frames[4 + i].paste(img.perspective(points), pos, below=True)
23
 
24
- frames: List[IMG] = []
25
  frames.append(raw_frames[0].image)
26
  for i in range(4):
27
  frames.append(raw_frames[1].image)
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def capoo_draw(images: list[BuildImage], texts, args):
13
  img = images[0].convert("RGBA").resize((175, 120), keep_ratio=True)
14
  params = (
15
  (((27, 0), (207, 12), (179, 142), (0, 117)), (30, 16)),
 
20
  points, pos = params[i]
21
  raw_frames[4 + i].paste(img.perspective(points), pos, below=True)
22
 
23
+ frames: list[IMG] = []
24
  frames.append(raw_frames[0].image)
25
  for i in range(4):
26
  frames.append(raw_frames[1].image)
meme_generator/memes/capoo_rip/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -9,7 +8,7 @@ from meme_generator.utils import save_gif
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
- def capoo_rip(images: List[BuildImage], texts, args):
13
  img = images[0].convert("RGBA").resize((150, 100), keep_ratio=True)
14
  img_left = img.crop((0, 0, 75, 100))
15
  img_right = img.crop((75, 0, 150, 100))
@@ -40,7 +39,7 @@ def capoo_rip(images: List[BuildImage], texts, args):
40
  raw_frames[i + 6].paste(img_left.perspective(points1), pos1, below=True)
41
  raw_frames[i + 6].paste(img_right.perspective(points2), pos2, below=True)
42
 
43
- new_frames: List[BuildImage] = []
44
  for i in range(3):
45
  new_frames += raw_frames[0:3]
46
  new_frames += raw_frames[3:]
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
+ def capoo_rip(images: list[BuildImage], texts, args):
12
  img = images[0].convert("RGBA").resize((150, 100), keep_ratio=True)
13
  img_left = img.crop((0, 0, 75, 100))
14
  img_right = img.crop((75, 0, 150, 100))
 
39
  raw_frames[i + 6].paste(img_left.perspective(points1), pos1, below=True)
40
  raw_frames[i + 6].paste(img_right.perspective(points2), pos2, below=True)
41
 
42
+ new_frames: list[BuildImage] = []
43
  for i in range(3):
44
  new_frames += raw_frames[0:3]
45
  new_frames += raw_frames[3:]
meme_generator/memes/capoo_rub/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -10,9 +9,9 @@ from meme_generator.utils import save_gif
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
- def capoo_rub(images: List[BuildImage], texts, args):
14
  img = images[0].convert("RGBA").square().resize((180, 180))
15
- frames: List[IMG] = []
16
  locs = [
17
  (178, 184, 78, 260),
18
  (178, 174, 84, 269),
@@ -27,4 +26,6 @@ def capoo_rub(images: List[BuildImage], texts, args):
27
  return save_gif(frames, 0.1)
28
 
29
 
30
- add_meme("capoo_rub", capoo_rub, min_images=1, max_images=1, keywords=["咖波蹭", "咖波贴"])
 
 
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
+ def capoo_rub(images: list[BuildImage], texts, args):
13
  img = images[0].convert("RGBA").square().resize((180, 180))
14
+ frames: list[IMG] = []
15
  locs = [
16
  (178, 184, 78, 260),
17
  (178, 174, 84, 269),
 
26
  return save_gif(frames, 0.1)
27
 
28
 
29
+ add_meme(
30
+ "capoo_rub", capoo_rub, min_images=1, max_images=1, keywords=["咖波蹭", "咖波贴"]
31
+ )
meme_generator/memes/capoo_say/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from PIL.Image import Image as IMG
5
  from pil_utils import BuildImage
@@ -11,7 +10,7 @@ from meme_generator.utils import save_gif
11
  img_dir = Path(__file__).parent / "images"
12
 
13
 
14
- def capoo_say_one_loop(text: str) -> List[IMG]:
15
  text_frame = BuildImage.new("RGBA", (80, 80))
16
  try:
17
  text_frame.draw_text(
@@ -39,7 +38,7 @@ def capoo_say_one_loop(text: str) -> List[IMG]:
39
  None,
40
  ]
41
 
42
- frames: List[IMG] = []
43
  for i in range(10):
44
  frame = BuildImage.open(img_dir / f"{i}.png")
45
  param = params[i]
@@ -52,7 +51,7 @@ def capoo_say_one_loop(text: str) -> List[IMG]:
52
  return frames
53
 
54
 
55
- def capoo_say(images, texts: List[str], args):
56
  frames = sum([capoo_say_one_loop(text) for text in texts], [])
57
  return save_gif(frames, 0.1)
58
 
 
1
  from pathlib import Path
 
2
 
3
  from PIL.Image import Image as IMG
4
  from pil_utils import BuildImage
 
10
  img_dir = Path(__file__).parent / "images"
11
 
12
 
13
+ def capoo_say_one_loop(text: str) -> list[IMG]:
14
  text_frame = BuildImage.new("RGBA", (80, 80))
15
  try:
16
  text_frame.draw_text(
 
38
  None,
39
  ]
40
 
41
+ frames: list[IMG] = []
42
  for i in range(10):
43
  frame = BuildImage.open(img_dir / f"{i}.png")
44
  param = params[i]
 
51
  return frames
52
 
53
 
54
+ def capoo_say(images, texts: list[str], args):
55
  frames = sum([capoo_say_one_loop(text) for text in texts], [])
56
  return save_gif(frames, 0.1)
57
 
meme_generator/memes/capoo_strike/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -9,7 +8,7 @@ from meme_generator.utils import FrameAlignPolicy, Maker, make_gif_or_combined_g
9
  img_dir = Path(__file__).parent / "images"
10
 
11
 
12
- def capoo_strike(images: List[BuildImage], texts, args):
13
  params = (
14
  (((0, 4), (153, 0), (138, 105), (0, 157)), (28, 47)),
15
  (((1, 13), (151, 0), (130, 104), (0, 156)), (28, 48)),
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
+ def capoo_strike(images: list[BuildImage], texts, args):
12
  params = (
13
  (((0, 4), (153, 0), (138, 105), (0, 157)), (28, 47)),
14
  (((1, 13), (151, 0), (130, 104), (0, 156)), (28, 48)),
meme_generator/memes/captain/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
  from pathlib import Path
2
- from typing import List
3
 
4
  from pil_utils import BuildImage
5
 
@@ -8,7 +7,7 @@ from meme_generator import add_meme
8
  img_dir = Path(__file__).parent / "images"
9
 
10
 
11
- def captain(images: List[BuildImage], texts, args):
12
  if len(images) == 2:
13
  images.append(images[-1])
14
 
 
1
  from pathlib import Path
 
2
 
3
  from pil_utils import BuildImage
4
 
 
7
  img_dir = Path(__file__).parent / "images"
8
 
9
 
10
+ def captain(images: list[BuildImage], texts, args):
11
  if len(images) == 2:
12
  images.append(images[-1])
13
 
meme_generator/memes/certificate/__init__.py CHANGED
@@ -1,6 +1,5 @@
1
  from datetime import datetime
2
  from pathlib import Path
3
- from typing import List
4
 
5
  import dateparser
6
  from pil_utils import BuildImage
@@ -20,7 +19,7 @@ class Model(MemeArgsModel):
20
  img_dir = Path(__file__).parent / "images"
21
 
22
 
23
- def certificate(images, texts: List[str], args: Model):
24
  time = datetime.now()
25
  if args.time and (parsed_time := dateparser.parse(args.time)):
26
  time = parsed_time
@@ -61,7 +60,9 @@ def certificate(images, texts: List[str], args: Model):
61
  try:
62
  frame.draw_text(
63
  (450, 850, 2270, 1080),
64
- texts[3] if len(texts) >= 4 else "  在本学年第一学期中表现优秀,被我校决定评为",
 
 
65
  allow_wrap=True,
66
  max_fontsize=80,
67
  min_fontsize=40,
 
1
  from datetime import datetime
2
  from pathlib import Path
 
3
 
4
  import dateparser
5
  from pil_utils import BuildImage
 
19
  img_dir = Path(__file__).parent / "images"
20
 
21
 
22
+ def certificate(images, texts: list[str], args: Model):
23
  time = datetime.now()
24
  if args.time and (parsed_time := dateparser.parse(args.time)):
25
  time = parsed_time
 
60
  try:
61
  frame.draw_text(
62
  (450, 850, 2270, 1080),
63
+ texts[3]
64
+ if len(texts) >= 4
65
+ else "  在本学年第一学期中表现优秀,被我校决定评为",
66
  allow_wrap=True,
67
  max_fontsize=80,
68
  min_fontsize=40,
meme_generator/memes/charpic/__init__.py CHANGED
@@ -1,5 +1,3 @@
1
- from typing import List
2
-
3
  from PIL import Image, ImageDraw
4
  from pil_utils import BuildImage
5
  from pil_utils.fonts import Font
@@ -8,7 +6,7 @@ from meme_generator import add_meme
8
  from meme_generator.utils import make_jpg_or_gif
9
 
10
 
11
- def charpic(images: List[BuildImage], texts, args):
12
  img = images[0]
13
  str_map = "@@$$&B88QMMGW##EE93SPPDOOU**==()+^,\"--''. "
14
  num = len(str_map)
 
 
 
1
  from PIL import Image, ImageDraw
2
  from pil_utils import BuildImage
3
  from pil_utils.fonts import Font
 
6
  from meme_generator.utils import make_jpg_or_gif
7
 
8
 
9
+ def charpic(images: list[BuildImage], texts, args):
10
  img = images[0]
11
  str_map = "@@$$&B88QMMGW##EE93SPPDOOU**==()+^,\"--''. "
12
  num = len(str_map)