21 Commits

Author SHA1 Message Date
XuChenXu
e1a20922c4 🔖 Version 2.6.0 2024-11-25 21:19:14 +08:00
XuChenXu
5e00605dfb 🎨 结构化代码 2024-11-25 21:18:09 +08:00
XuChenXu
833cdb4a75 💄 还是人性化 2024-11-25 21:16:37 +08:00
XuChenXu
29b4598e5f 💄 人性化提示 2024-11-25 21:02:04 +08:00
XuChenXu
10cc5cf278 关键词支持正则 2024-11-25 21:00:00 +08:00
XuChenXu
ed167a56b5 🔖 Version 2.5.2 2024-11-25 20:51:32 +08:00
XuChenXu
b40c7be1f9 🐛 修复转发消息 2024-11-25 20:50:50 +08:00
XuChenXu
07f85be527 🔖 Version 2.5.1 2024-11-24 20:15:43 +08:00
XuChenXu
f2cdfe7f8a 📝 usage 2024-11-24 20:12:32 +08:00
XuChenXu
e0ca7907c0 kkb! 2024-11-24 20:07:42 +08:00
XuChenXu
53b2a33923 合并转发开关! 2024-11-24 19:39:07 +08:00
XuChenXu
4e339bcdb1 🔖 Version 2.4.6 2024-11-20 23:29:39 +08:00
XuChenXu
0b85f6fb34 remove requests 2024-11-20 23:29:23 +08:00
XuChenXu
83d9cda2eb 🥚 Update README.md 2024-11-11 12:56:38 +08:00
XuChenXu
dfde832dba 📝 fix readme 2024-11-10 21:11:58 +08:00
enKl03B
987b34e282 🔀 📝 Update README.md (#44) 2024-11-10 21:03:01 +08:00
XuChenXu
49ce2ffb7c 🔖 Version 2.4.5 2024-11-09 11:28:13 +08:00
XuChenXu
fcce94250c 🔀 Merge pull request #43 from ChenXu233/feat#20#39
Feat#20#39
2024-11-09 11:25:20 +08:00
XuChenXu
53e52eabb5 🔀 Merge branch 'master' into feat#20#39 2024-11-09 11:24:16 +08:00
XuChenXu
633ffbde9a 🎨 格式化代码 2024-11-09 11:20:53 +08:00
XuChenXu
d42140cc95 初步实现:关键词筛选后排行 #20 2024-11-02 14:15:24 +08:00
9 changed files with 211 additions and 143 deletions

View File

@@ -19,6 +19,7 @@
"pygal", "pygal",
"sqlalchemy", "sqlalchemy",
"timecost", "timecost",
"uninfo",
"userinfo", "userinfo",
"whereclause", "whereclause",
"xaxis", "xaxis",

View File

@@ -26,15 +26,16 @@ nonebot-plugin-dialectlist
## 💿 安装 ## 💿 安装
通过`pip``nb`安装 通过`pip``nb`安装
- 通过 pip 安装
```bash
pip install nonebot-plugin-dialectlist
```
- 通过 nb-cli 安装
```bash
nb plugin install nonebot-plugin-dialectlist
```
>**通过 pip **安装
`pip install nonebot-plugin-dialectlist`
>**通过 nb **安装
`nb plugin install nonebot-plugin-dialectlist`
### ✅ 插件依赖于 ### ✅ 插件依赖于
@@ -76,6 +77,8 @@ __注意__
### 🎨一般用法 ### 🎨一般用法
#### B话榜
-`/B话榜` ————看看有史以来(机器人存在以来)群友们发了多少消息! (好像没写) -`/B话榜` ————看看有史以来(机器人存在以来)群友们发了多少消息! (好像没写)
-`/今日B话榜` ————看看今天的群友发了多少消息! -`/今日B话榜` ————看看今天的群友发了多少消息!
@@ -94,11 +97,34 @@ __注意__
-`/历史B话榜` ————看看历史上(机器人存在以来)的群友发了多少消息! -`/历史B话榜` ————看看历史上(机器人存在以来)的群友发了多少消息!
#### 看看B话kkb
-`/看看B话 [@某人|QQ号]` ————看看这个b人在这个b群发了多少b话
### 🚀进阶用法 ### 🚀进阶用法
`/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间} {群号}` #### B话榜
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678` `/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} {群号} {关键词}`
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678 女装`
也可以 `/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} -g {群号} -k {关键词}`
以下调用方法均合法:
`/今日B话榜 -g 12345678 -k 女装`
`/昨日B话榜 -k 女装`
`/本周B话榜 -g 12345678`
#### 看看B话
`/看看B话 {@|QQ号} {群号?} {关键词?}`
以下调用方法均合法:
`/kkb 114514 1919810 ♂`
`/kkb @man -k ♂`
## 💪 目前支持的平台 ## 💪 目前支持的平台
@@ -134,6 +160,9 @@ __注意__
</details> </details>
![9c149e99ca747c13892be3f9fbeedf31](https://github.com/user-attachments/assets/d02d108c-1ab3-4ad1-9123-c1a98862e627)
### 💕感谢 ### 💕感谢
本插件的__init__.py 中的处理函数参考了词云中的方法 ~~(其实大部分都是 Ctrl+C Ctr+V~~ 本插件的__init__.py 中的处理函数参考了词云中的方法 ~~(其实大部分都是 Ctrl+C Ctr+V~~
@@ -158,13 +187,18 @@ __注意__
- [x] 私聊的查询(超级用户可以任意查询群聊的信息)一半完成 - [x] 私聊的查询(超级用户可以任意查询群聊的信息)一半完成
- [ ] 特殊的储存方案优化消息统计 - [x] 特殊的储存方案优化消息统计
- [ ] 查询带某关键词的消息量 - [x] 查询带某关键词的消息量
- [x] 合并转发
待补充。..... 待补充。.....
## 📖版本 ## 📖版本日志
<details>
<summary>点我展开</summary>
### V1.0 ### V1.0
@@ -199,5 +233,11 @@ __注意__
- 优化代码,添加一些新的可配置项。 - 优化代码,添加一些新的可配置项。
### V2.4
- 添加一些新的可配置项。
</details>
### 👾题外话 ### 👾题外话
~~整个项目快被我写成屎山了~~ ~~整个项目快被我写成屎山了~~

View File

@@ -5,6 +5,7 @@ require("nonebot_plugin_apscheduler")
require("nonebot_plugin_htmlrender") require("nonebot_plugin_htmlrender")
require("nonebot_plugin_userinfo") require("nonebot_plugin_userinfo")
require("nonebot_plugin_alconna") require("nonebot_plugin_alconna")
require("nonebot_plugin_uninfo")
require("nonebot_plugin_cesaa") require("nonebot_plugin_cesaa")
import re import re
@@ -23,13 +24,17 @@ from nonebot.params import Arg, Depends
from nonebot.adapters import Bot, Event from nonebot.adapters import Bot, Event
from nonebot.plugin import PluginMetadata, inherit_supported_adapters from nonebot.plugin import PluginMetadata, inherit_supported_adapters
from nonebot_plugin_alconna import ( from nonebot_plugin_alconna import (
At,
Args, Args,
Field,
Match,
Option, Option,
Alconna, Alconna,
on_alconna, on_alconna,
) )
from nonebot_plugin_chatrecorder import get_message_records from nonebot_plugin_chatrecorder import get_message_records
from nonebot_plugin_session import Session, SessionIdType, extract_session from nonebot_plugin_session import Session, SessionIdType, extract_session
from nonebot_plugin_uninfo import Uninfo
from .storage import get_cache, build_cache from .storage import get_cache, build_cache
from .config import Config, plugin_config from .config import Config, plugin_config
@@ -85,6 +90,63 @@ async def _build_cache(bot: Bot, event: Event):
await saa.Text("重建缓存完成。").send(reply=True) await saa.Text("重建缓存完成。").send(reply=True)
b_cmd = on_alconna(
Alconna(
"看看B话",
Args["at", [str, At], Field(completion=lambda: "请想要查询的人的QQ号")],
Option("-g|--group_id", Args["group_id?", str]),
Option("-k|--keyword", Args["keyword?", str]),
),
aliases={"kkb"},
use_cmd_start=True,
)
@b_cmd.handle()
async def handle_b_cmd(
at: Match[str | At],
group_id: Match[str],
keyword: Match[str],
uninfo: Uninfo,
session: Session = Depends(extract_session),
):
id = at.result
if isinstance(id, At):
id = id.target
if group_id.available:
gid = group_id.result
else:
gid = session.id2
if not gid:
await b_cmd.finish("请指定群号。")
if keyword.available:
keywords = keyword.result
else:
keywords = None
messages = await get_message_records(
id1s=[id],
id2s=[gid],
id_type=SessionIdType.GROUP,
include_bot_id=False,
include_bot_type=False,
types=["message"], # 排除机器人自己发的消息
exclude_id1s=plugin_config.excluded_people,
)
d = msg_counter(messages, keywords)
rank = got_rank(d)
if not rank:
await b_cmd.finish(
f"该用户在群“{uninfo.scene.name}”关于“{keyword}”的B话数量为0。"
)
await saa.Text(
f"该用户在群“{uninfo.scene.name}”关于“{keyword}”的B话数量为{rank[0][1]}"
).send(reply=True)
rank_cmd = on_alconna( rank_cmd = on_alconna(
Alconna( Alconna(
"B话榜", "B话榜",
@@ -93,6 +155,7 @@ rank_cmd = on_alconna(
str, str,
], ],
Option("-g|--group_id", Args["group_id?", str]), Option("-g|--group_id", Args["group_id?", str]),
Option("-k|--keyword", Args["keyword?", str]),
behaviors=[SameTime()], behaviors=[SameTime()],
), ),
aliases={"废话榜"}, aliases={"废话榜"},
@@ -129,6 +192,7 @@ async def _group_message(
type: Optional[str] = None, type: Optional[str] = None,
time: Optional[str] = None, time: Optional[str] = None,
group_id: Optional[str] = None, group_id: Optional[str] = None,
keyword: Optional[str] = None,
): ):
t1 = t.time() t1 = t.time()
state["t1"] = t1 state["t1"] = t1
@@ -140,6 +204,8 @@ async def _group_message(
if group_id: if group_id:
state["group_id"] = group_id state["group_id"] = group_id
state["keyword"] = keyword
if not type: if not type:
await rank_cmd.finish(__plugin_meta__.usage) await rank_cmd.finish(__plugin_meta__.usage)
@@ -226,7 +292,11 @@ async def handle_rank(
if not id: if not id:
await saa.Text("没有指定群哦").finish() await saa.Text("没有指定群哦").finish()
keyword = state["keyword"]
if plugin_config.counting_cache: if plugin_config.counting_cache:
if keyword:
await saa.Text("已开启缓存~缓存不支持关键词查询哦").finish()
t1 = t.time() t1 = t.time()
raw_rank = await get_cache(start, stop, id) raw_rank = await get_cache(start, stop, id)
logger.debug(f"获取计数消息花费时间:{t.time() - t1}") logger.debug(f"获取计数消息花费时间:{t.time() - t1}")
@@ -242,11 +312,13 @@ async def handle_rank(
time_stop=stop, time_stop=stop,
exclude_id1s=plugin_config.excluded_people, exclude_id1s=plugin_config.excluded_people,
) )
raw_rank = msg_counter(messages) raw_rank = msg_counter(messages, keyword)
logger.debug(f"获取计数消息花费时间:{t.time() - t1}") logger.debug(f"获取计数消息花费时间:{t.time() - t1}")
if not raw_rank: if not raw_rank:
await saa.Text("明明这个时间段都没有人说话怎么会有话痨榜呢?").finish() await saa.Text(
"没有获取到排行榜数据哦,请确认时间范围和群号是否正确或者关键词是否存在~"
).finish()
rank = got_rank(raw_rank) rank = got_rank(raw_rank)
ids = await persist_id2user_id([int(i[0]) for i in rank]) ids = await persist_id2user_id([int(i[0]) for i in rank])
@@ -259,8 +331,13 @@ async def handle_rank(
logger.debug(f"获取用户信息花费时间:{t.time() - t1}") logger.debug(f"获取用户信息花费时间:{t.time() - t1}")
string: str = "" string: str = ""
if plugin_config.show_text_rank: if plugin_config.show_text_rank:
if keyword:
string += f"关于{keyword}的话痨榜结果:\n"
else:
string += "话痨榜:\n"
for i in rank2: for i in rank2:
logger.debug(i.user_name) logger.debug(i.user_name)
for i in range(len(rank2)): for i in range(len(rank2)):
@@ -286,4 +363,7 @@ async def handle_rank(
if not msg: if not msg:
await saa.Text("你把可视化都关了哪来的排行榜?").finish() await saa.Text("你把可视化都关了哪来的排行榜?").finish()
if plugin_config.aggregate_transmission:
await saa.AggregatedMessageFactory([msg]).finish()
else:
await msg.finish(reply=True) await msg.finish(reply=True)

View File

@@ -13,6 +13,7 @@ class ScopedConfig(BaseModel):
counting_cache: bool = False # 计数缓存(能够提高回复速度) counting_cache: bool = False # 计数缓存(能够提高回复速度)
excluded_people: List[str] = [] # 排除的人的QQ号 excluded_people: List[str] = [] # 排除的人的QQ号
use_user_info_cache: bool = False # 是否使用用户信息缓存 use_user_info_cache: bool = False # 是否使用用户信息缓存
aggregate_transmission: bool = False # 是否聚合转发消息
timezone: Optional[str] = "Asia/Shanghai" # 时区,影响统计时间 timezone: Optional[str] = "Asia/Shanghai" # 时区,影响统计时间
string_suffix: str = "统计花费时间:{timecost}" # 消息格式后缀 string_suffix: str = "统计花费时间:{timecost}" # 消息格式后缀
template_path: str = "./template/rank_template.j2" # 模板路径 template_path: str = "./template/rank_template.j2" # 模板路径

View File

@@ -2,8 +2,59 @@ from inspect import cleandoc
__usage__ = cleandoc( __usage__ = cleandoc(
""" """
/今日B话榜 查看今天群里谁B话最多。 快速调用:
/日B话榜 顾名思义 /日B话榜 ————看看今天群友发了多少消息
以此类推,有本周,本月,上周,上月,年度排行榜。
### 🎨一般用法
#### B话榜
-`/B话榜` ————看看有史以来(机器人存在以来)群友们发了多少消息! (好像没写)
-`/今日B话榜` ————看看今天的群友发了多少消息!
-`/昨日B话榜` ————看看昨天的群友发了多少消息!
-`/前日B话榜` ————看看前天的群友发了多少消息!
-`/本周B话榜` ————看看本周的群友发了多少消息!
-`/上周B话榜` ————看看上周的群友发了多少消息!
-`/本月B话榜` ————看看这个月的群友发了多少消息!
-`/年度B话榜` ————看看今年的群友发了多少消息!
-`/历史B话榜` ————看看历史上(机器人存在以来)的群友发了多少消息!
#### 看看B话kkb
-`/看看B话 [@某人|QQ号]` ————看看这个b人在这个b群发了多少b话
### 🚀进阶用法
#### B话榜
`/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} {群号} {关键词}`
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678 女装`
也可以 `/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} -g {群号} -k {关键词}`
以下调用方法均合法:
`/今日B话榜 -g 12345678 -k 女装`
`/昨日B话榜 -k 女装`
`/本周B话榜 -g 12345678`
#### 看看B话
`/看看B话 {@|QQ号} {群号?} {关键词?}`
以下调用方法均合法:
`/kkb 114514 1919810 ♂`
`/kkb @man -k ♂`
""" """
) )

View File

@@ -1,9 +1,10 @@
import os import os
import re
import httpx import httpx
import asyncio import asyncio
import unicodedata import unicodedata
from typing import Dict, List from typing import Dict, List, Optional
from sqlalchemy import or_, select from sqlalchemy import or_, select
from sqlalchemy.sql import ColumnElement from sqlalchemy.sql import ColumnElement
@@ -77,7 +78,9 @@ async def persist_id2group_id(ids: List[str]) -> List[str]:
return [i.id2 for i in records] return [i.id2 for i in records]
def msg_counter(msg_list: List[MessageRecord]) -> Dict[str, int]: def msg_counter(
msg_list: List[MessageRecord], keyword: Optional[str]
) -> Dict[str, int]:
"""### 计算每个人的消息量 """### 计算每个人的消息量
Args: Args:
@@ -92,6 +95,11 @@ def msg_counter(msg_list: List[MessageRecord]) -> Dict[str, int]:
logger.info("wow , there are {} msgs to count !!!".format(msg_len)) logger.info("wow , there are {} msgs to count !!!".format(msg_len))
for i in msg_list: for i in msg_list:
# logger.debug(f"processing msg {i.plain_text}")
if keyword:
match = re.search(keyword, i.plain_text)
if not match:
continue
try: try:
lst[str(i.session_persist_id)] += 1 lst[str(i.session_persist_id)] += 1
except KeyError: except KeyError:

8
pdm.lock generated
View File

@@ -5,7 +5,7 @@
groups = ["default", "Test", "dev"] groups = ["default", "Test", "dev"]
strategy = ["inherit_metadata"] strategy = ["inherit_metadata"]
lock_version = "4.5.0" lock_version = "4.5.0"
content_hash = "sha256:882dfc18d7454ced6bb1f46e04172b14b83f90157475153bf0fff7b13e4d41a2" content_hash = "sha256:41c1ac2ec7d9ce3eb477f442e6ccbeda65fb25eb4f22cd696d361dae97cb065c"
[[metadata.targets]] [[metadata.targets]]
requires_python = ">=3.9,<3.13" requires_python = ">=3.9,<3.13"
@@ -267,7 +267,7 @@ name = "charset-normalizer"
version = "3.3.2" version = "3.3.2"
requires_python = ">=3.7.0" requires_python = ">=3.7.0"
summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
groups = ["default", "dev"] groups = ["dev"]
files = [ files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
@@ -1876,7 +1876,7 @@ name = "requests"
version = "2.32.3" version = "2.32.3"
requires_python = ">=3.8" requires_python = ">=3.8"
summary = "Python HTTP for Humans." summary = "Python HTTP for Humans."
groups = ["default", "dev"] groups = ["dev"]
dependencies = [ dependencies = [
"certifi>=2017.4.17", "certifi>=2017.4.17",
"charset-normalizer<4,>=2", "charset-normalizer<4,>=2",
@@ -2383,7 +2383,7 @@ name = "urllib3"
version = "2.2.1" version = "2.2.1"
requires_python = ">=3.8" requires_python = ">=3.8"
summary = "HTTP library with thread-safe connection pooling, file post, and more." summary = "HTTP library with thread-safe connection pooling, file post, and more."
groups = ["default", "dev"] groups = ["dev"]
files = [ files = [
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},

View File

@@ -1,13 +1,12 @@
[project] [project]
name = "nonebot-plugin-dialectlist" name = "nonebot-plugin-dialectlist"
version = "2.4.4" version = "2.6.0"
description = "看看你群群友有多能说" description = "看看你群群友有多能说"
authors = [ authors = [
{name = "Chen_Xu233", email = "woyerpa@outlook.com"}, {name = "Chen_Xu233", email = "woyerpa@outlook.com"},
] ]
dependencies = [ dependencies = [
"nonebot-plugin-chatrecorder>=0.6.0", "nonebot-plugin-chatrecorder>=0.6.0",
"requests>=2.32.3",
"nonebot-plugin-orm[default]", "nonebot-plugin-orm[default]",
"nonebot-plugin-apscheduler>=0.4.0", "nonebot-plugin-apscheduler>=0.4.0",
"nonebot-plugin-alconna>=0.50.2", "nonebot-plugin-alconna>=0.50.2",

View File

@@ -1,112 +0,0 @@
import PIL
import abc
from typing import List, Tuple, Union, Iterable
from PIL import Image, ImageDraw, ImageFont
class BaseElement(abc.ABC):
def __init__(self, box:Union[Iterable[int],Iterable[float]] = (0, 0, 0, 0)):
self.box = [float(i) for i in box]
self.ux = None
self.ly = None
self.dx = None
self.ry = None
self.hight = None
self.width = None
self.image = None
self.position = (self.ux, self.ly)
self.size = (self.width, self.hight)
@abc.abstractmethod
def render(self):
raise NotImplementedError
def get_box(self):
return self.box
def set_box(self, box:Tuple[float, float, float, float]):
self.box = box
def get_size(self):
self.size = (self.width, self.hight)
return self.size
def get_position(self):
self.position = (self.ux, self.ly)
return self.position
class Board(BaseElement):
def __init__(self, width, hight):
self.width = width
self.hight = hight
self.image = Image.new('RGBA', (self.width, self.hight), (255, 255, 255, 0))
self.elements = []
def render(self):
for i in self.elements:
i.ux = self.width * i.box[0]
i.ly = self.hight * i.box[1]
i.dx = self.width * i.box[2]
i.ry = self.hight * i.box[3]
i.hight = i.dx - i.ux
i.width = i.ry - i.ly
i.position = (i.ux, i.ly)
image = i.render()
self.image.paste(image, i.get_position())
def add_element(self, element:BaseElement):
self.elements.append(element)
class Container(BaseElement):
def __init__(self, box:Union[Iterable[int],Iterable[float]] = (0, 0, 0, 0)):
super().__init__(box)
self.elements:List[BaseElement] = []
def render(self,size:Tuple[int, int] = (100, 100)):
if not self.width and self.hight:
self.width = size[0]
self.hight = size[1]
if not self.image:
self.image = Image.new('RGBA', (self.width, self.hight), (255, 255, 255, 0))
for i in self.elements:
image = i.render()
self.image.paste(image, i.get_position())
class Element(BaseElement):
# class BaseContainer(abc.ABC):
# def __init__(self, width:int, height:int):
# self.width = width
# self.height = height
# self.image = Image.new('RGBA', (self.width, self.height), (255, 255, 255, 0))
# @abc.abstractmethod
# def render(self):
# raise NotImplementedError
# def set_image(self,image):
# self.image = image
# class Elements():
# def __init__(self, width:int, height:int):
# self.width = width
# self.height = height
# self.image = Image.new('RGBA', (self.width, self.height), (255, 255, 255, 0))
# def render(self):
# return self.image
# class Container(BaseContainer):
# def __init__(self, width:int, height:int,elements:List[Union['Container',Elements]] = []):
# super().__init__(width, height)
# self.elements = elements
# def render(self):
# for i in self.elements:
# image = i.render()
# self.image.paste(image, i.position)
# return self.image