├── .github └── dependabot.yml ├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── _config.yml ├── bot.py ├── bot.py.save ├── config_bot.py ├── doc_assets ├── README.html ├── custom_reply.md ├── evaler.md ├── notifyme.md ├── remotely.md ├── repeater.md ├── signin.md ├── start.md └── weather.md ├── plugins ├── _googlesearch │ ├── __init__.py │ └── data_source.py ├── _sign_in │ ├── __init__.py │ ├── execute.py │ └── greets.py ├── azurlane_buildship_simulator │ ├── __init__.py │ ├── constants.py │ └── simulator.py ├── baidutrend │ ├── __init__.py │ └── data_source.py ├── custom_reply │ ├── __init__.py │ └── group_data.json ├── evaler │ ├── __init__.py │ └── data_source_glot_run.py ├── exec.py ├── gamble │ ├── __init__.py │ └── diceroll.py ├── notifyme │ └── __init__.py ├── personality │ └── __init__.py ├── remotely │ └── __init__.py ├── repeater │ └── __init__.py ├── request_handler.py ├── song │ ├── __init__.py │ └── data_source.py ├── test_rp │ └── __init__.py ├── usage.py └── weather │ ├── __init__.py │ ├── china_city_list_source_amap.py │ ├── china_city_list_source_amap.txt │ ├── data_source_amap.py │ └── data_source_openweather.py ├── requirements.txt └── utils_bot ├── __init__.py ├── command_ops.py ├── datetime.py ├── logging.py ├── msg_ops.py ├── string_ops.py └── typing.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: aiosqlite 11 | versions: 12 | - 0.16.1 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # editor 107 | .vscode/ 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 cos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## sendo erika on [NoneBot](https://github.com/richardchien/nonebot) 2 | [![License](https://img.shields.io/github/license/richardchien/nonebot.svg)](LICENSE) ![Python](https://img.shields.io/badge/python-3.7%2B-blue.svg) 3 | 4 | ### Description 5 | I am a QQ group chatting bot based on Coolq, Coolq http api and Nonebot which takes advantages on Python's [asyncio](https://docs.python.org/3/library/asyncio.html) mechanisms thus supporting a high volume of message i/o. I do provide useful plugins extended from Nonebot framework to ensure the bot operates at an acceptable and useable level. 6 | Like what was described on Nonebot, I only run on over Python 3.7+ and CoolQ HTTP plugin v4.7+. 7 | 8 | ### Plugins 9 | I do 10 | * detect keywords from group chats and reply from customized settings 11 | * be controlled from owner directly to send messages 12 | * repeat 13 | * sign in 14 | * notify you to wake up from bed at 6 am 15 | * search weather data and etc 16 | 17 | You can freely only absorb part of this repo to merge it into your own coolq applications. 18 | ### XXXXXXXXXXXX 19 | * First follow https://cqp.cc/ and instructions to get CoolQ ready then 20 | * Start the CoolQ application and makes sure it is working properly. 21 | *** If you use docker, go here: https://github.com/CoolQ/docker-wine-coolq to deploy a docker application by 22 | ``` 23 | mkdir coolq && cd coolq 24 | docker run --rm -p 9000:9000 -v `pwd`:/home/user/coolq coolq/wine-coolq 25 | ``` 26 | * Follow https://cqhttp.cc/ to get http api plugin enabled on CoolQ. 27 | * then refer to https://nonebot.cqp.moe/ to get familiar with Nonebot 28 | * have required dependency: 29 | ``` 30 | pip3 install nonebot 31 | ... 32 | ``` 33 | * Plugin documentation is available, see it and finish initialization. 34 | 35 | ### Update since August 1 2020 36 | Since Coolq is dead, please refer to [this issue](https://github.com/nonebot/nonebot/issues/217) for alternative ways of deploying it. 37 | 38 | ### Update since March 20 2020 39 | [NoneBot](https://github.com/richardchien/nonebot) has updated to v3.5.0, abandoning the use of `Context_T` and discarding Python 3.6. As a result, this bot, after bumping versions, only runs above Python 3.7. 40 | 41 | ### Related 42 | * [scripter for sendo erika](https://github.com/cleoold/scripter-for-sendo-erika) 43 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cleoold/sendo-erika/61dcc6d7e01a59e3f454a90e3b3094eb571ff312/__init__.py -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | 3 | import nonebot 4 | import config_bot as config 5 | 6 | if __name__ == '__main__': 7 | nonebot.init(config) 8 | nonebot.load_plugins( 9 | path.join(path.dirname(__file__), 'plugins'), 10 | 'plugins' 11 | ) 12 | nonebot.run(host=config.HOST, port=config.PORT) 13 | -------------------------------------------------------------------------------- /bot.py.save: -------------------------------------------------------------------------------- 1 | import nonebot 2 | 3 | if __name__ == '__main__': 4 | nonebot.init() 5 | nonebot.load_builtin_plugins() 6 | nonebot.run(host='', port=8080) 7 | -------------------------------------------------------------------------------- /config_bot.py: -------------------------------------------------------------------------------- 1 | from nonebot.default_config import * 2 | 3 | from utils_bot.datetime import timedelta 4 | 5 | ## ::::::::::::::::..::::::::::::::::::::::::::::::::::::::::::::.............::::::::::::::: 6 | ## :::::::,,,,,::::::..:::,::::::::::::::::,,,:::::,,,,,,:,,:::..::::::::::::...:::::::::::,: 7 | ## ::,,,,,:::::::::::::..:::,:,:::::,,,:,::,,::,,,,::::::,::::.::::::::::::::::...:::::,,,,,: 8 | ## ::,,:::::::::::::,:::::::,::::::::::::::::::,,,,;;;,,,::::.::::,:::::::::,::::.:::::::::,: 9 | ## ::::,,,:::::,::::,,:::.::::::::::,,,:,::,,;i,::::,,::,;;:::::,::::::::::::::::::.:::::::,: 10 | ## :::::::::::::,,,:,;;ii;;,.::,::::,::,:;;::::::t,:,,,,:,:,;;,::::::::::::::::::::..::,,,:,: 11 | ## ::::::::::::,:,;:,:::,::::;:::::,,,;i,:::::::i:;,,,::,,,::,:;t::::::::::::::::::::.:::::,: 12 | ## :::::::,:,:,.;,::,:,::,,,::;:::,:ii;,:::,::,;:,:;,,,,::::::,,it;::::,::::::::::::::.:,::,: 13 | ## ::::::::,:,,i,:,,::,,,;,,,,:;,,,,:.:::::::,:;::,;i,,,,:::::;;;;,;,:,:::::::::::::::.::::,: 14 | ## :::::::,:,:;::,,:;iiii,,,,;;,i:.,;;,:::::,:;:::,,:;,,,:::::,:,,,:,;:,:::::::::::::::.:::,: 15 | ## ::::::::,:;,,:,;i;,::,,,,::,;t,,ii,:::::,,,;::,:,,:,,,:::::,:,,:,,,;:,:::::::::::,:::.:::: 16 | ## :::::::::;,:,i,,,::,:,:,,;i;:.;i::::::,,,,i::,,,,,,,,,:::::::::,::,:,;::::::::::::,::.:::: 17 | ## :::::::,;,:i;:,::,,,:,;iti,:.,,.:.:::;:,,;,,,,,,,,,,,,,:,:::::::,::,:,;:,:::::::::,::..::: 18 | ## ::::::::;:i,:,,,,::,;i;i:,i..:.:.:::;:,:,i:,,,,,,,,,,,,,,,,::,,:,:,:,:;,:,,,,:::::,:::.::: 19 | ## ::::::,,,i;:,,:,,:,ii,;:ii,,,:.:...;:,;,;;,:,,,,,,,,,;,,,,,,::;;,:,,,::;,::,,::::,::::.::: 20 | ## :::::,:;;,:,,,,,,;i;:;:t;,,;...:.:;:,;,;i,::,,,,,,,;,,;:,,,,,,,,i,::::,,;,,:,:::::::::.::: 21 | ## ::::,:,;,:,,,,,,,i,:;,j,.;,.....:.,:;;:i;,::,:,,,,,;,,;,,;,:,,:,:,;:,,::;,,:::::::::::.::: 22 | ## :::::;,,:,,:,::;i::;;i:::; . .:.;,;,.,i;:::,:,,,,,,;,;;:i,,,;,,:,:i;,:,:;:,::::::::,::.:: 23 | ## :,:,,,,::::,::,i:,;;t;:.;:....: ,:;,,:i;;::,:::,,,;,;;i,:t,,,;::,:,,t:::,;::,::::::::::.:: 24 | ## :::,,,,:,::,,,t,,,it;,:;: :..: .;.;;.,t,,::,:::,,,i,i,i,,t,,,,::;,,,;,::,,,:,::::::::::::: 25 | ## ::,,::,::::::i,,,;i;;;,;.:. :..,:,,,.;i;::.,:::;,,j,t;i,,t;:,,,,,;:,;;::,,;:::::::::::.::: 26 | ## ::,:::,:::::,i:,,i;;,:i::,..:.,;.;::.ti;::.;::,,,;j,ti;,,j;,;,,,,i:,it,::,i,::::::::::.::: 27 | ## :;,,:,,,:,::i:,:;;,;,,,:;.:,.,;.i;; .ftt.:.;:,;:;t;t;tt,:j;,,,;,:;,i;i,:::i::,,,:,::::.::: 28 | ## :i::::,,:,,,;:,:t:;;:t:;,:,i,;i:tji.,ttt:..i:i,,ij:L,jj,,j;;,,i,,,;tji;::,t::,:,:::,::.::: 29 | ## ,,:::::,:,:;:,:;t,i,;j:;::;,i,,tijt,tijj:..i:i:it;ijtft,;i;;,,i,,,ittji::it::,::,tG;:.:::: 30 | ## ;,,,:,::,,:;:,,t;;i:ij,;:;,,;i:jttj:titj: i:,;ij:f;tfj,t,;i,;i:,:itttt;;ii:::;fDEL,:.:::: 31 | ## ,::::::::::,:,:t,i;;jt;;,;i;ijifjtt,ttif,..t;,;f,tj:fft,j:;i,;i:,,ijttjiitt:;LGDELj:.::::: 32 | ## :,:::::::::,:,;i,i;iL;i,;;ttijjtttLLitif,..j;;t;ijtjtft:j:;i,,;:,,ifttjfiittDGDEGL;:.::::: 33 | ## :::::::::::,,:t,:titf;t,iitjfijf,ititG;f;.:j;titti,t;ti;i:;,,i,,,:tLttfLfiDEDEDLGt::::,,:: 34 | ## ::::::::::::,:i:,t;tG,t;t;tLjt;f,ttt;Liji.:jifij;tttii.jj,i:,j,;,:tGjjjfGLEEEDLLL,::::,::: 35 | ## :,::::::::,:,:i:,tjtL;iii;Lftj;j:i,jif;fi.;titf,;ijit,j,f,t,;G:t;,tDjjjLEEDGGfjD;.,::::::: 36 | ## ::::::::::::,,i::tttL;;tiiGjiL:tjL:jit;Lt.iiGf,:jf;;;iL;f,t;iL:Lt,tEfjfLDGDGfjjj:::::::::: 37 | ## :,,,::::,::::;,,:t;jfi;tijLLiLfWWWWLi,;fj:iiG;,tjititftiL;ttjj:fj,jKLjjLDGGjjfL:,::::::::: 38 | ## :,::::::,::,,i:,,f;jfi;ttGfLtDWEt#WWf:iii:ifL,,LjtijDLGGfitifjijt;jKDfjLEfLfffD,:,,:::::,: 39 | ## :,::::::,::,:;:,,f,fititLtjGfWK.GWW#Wi,:;tijtit,;;j;G;;tfLLLfiGftfjELffLEfDLGEL,,,:,::::,: 40 | ## :,::::::,::,:;::,j,j;tttG;fDLKf t,WWWt:.,itt;t:;,;,,j;ifjfjfL;LfiLEDEGfLDEKELGD,,:::::::,: 41 | ## :,:::::::::,,;::;j,j,jttf,DGGft..:KWW;.::.f,i,,:,;::tttfLtfiffLf;GKDfLGEEKGEEEt;:,,:::::,: 42 | ## :::::::::::::,::,i:i;fijL;EjL:i DKKKK::...i::;,,;jLGGGjiLL;LtfDjtEEfjffGKEffDDt,,:::::::,: 43 | ## :,:::::::::::,:,,;,,;itLGtDjL:, GGEDE.:....:.::GW######KLijtftLiLEGjfjfGWGffGDj,,:::::::,: 44 | ## :,::::::::::,,:,,,,;,:jDDtDLL:: fLEDL........::Dt,#######fLtiGjiEELjjfGEKGjfGDf,,:::::::,: 45 | ## :,,:,::::::::,,::,,:,,GDDjLLf,:.;LWDt.........:,,,##W####Wi;GiiGEELffGDEEDffLDf:,:::::::,: 46 | ## ,,,::::::::::,,,,,,:,;DDEDftj,,:,DfG:............::DWWW###DjtjiEKDLjGEKGEEGfLDL:,:::::::,: 47 | ## ;,:.:,::::::::,:::,,:fDDEEL;t,:,:jEG ............, fWWKW#W#t;fGDEDfGKEKLDEEfLDG,::::::::,: 48 | ## ,,,:.::::::::::,,,:,;LEDEDi;;;,::::............. jKKKKEK#K#fjtGDDDDEKGEDDLEDLDf,::::::::,: 49 | ## :;;,.:,:,:,::::::,,:jfDDEf:t,:::::...............jDWKDGKWE#GLG;DEEDKfEfGDGGEDDj,,:::,:::,: 50 | ## ,:,i,.:,:,:::::::,,;;,;;f,tjt::...... :.:....... ;DK#WEGLG;ttjfGGjELLDGfDDfLGji,:,::::::,: 51 | ## ,::,;:.,,:,::::::,,:,,,L,;jtj.........i,:.......::KfEDfK;,;tjfDfjGEjGGGfDDfffG;,,,::::::,: 52 | ## :,,:;,..;:,::::,:::,;,f;itt;L;........,,:.......:;EDfLGG;;tGjLffjKDjGjGLDDLjjjL:,,::::::,: 53 | ## ,;,,,;:.:,,,:,::,::,:j;itj;iff:......:........:::::jDEGi;,GLDGffDDjLLjfLDDGLGGft:,::::::,: 54 | ## .:i,;,,:.:;:,:::::,;;i:jtiittLi.:.............::::,;;;;;iLGLGjfLDLfL;tLLGLjtjGjj:,,:::::,: 55 | ## .:.i,,i,:.,:;,,::,;,,,ititif;ft;.....iE,:.......::,;;iitjfiGfffDGL;fiiLjtt;;ttLij;::::::,: 56 | ## ;,::.i,i,::,,,:,,,:;::titiL;,jjj;..:.:LfffD,...::::,,tf;ttLDffGLtGttGi;jfiiii;ttj;,i;,,:,: 57 | ## i;,::.ii,::.,,,,,:,:,:;tjLji;iGtt,:...jtjfL,....:::,jiififL,tGD;iLLijLi;ititt;,,itti,,,,,: 58 | ## :;;,:::tj,::.,i,:,,.,:ttjt;Gf;Lj,j...:;ttjj.....:::j,ij;jt,ifWDf,;KjijLi,;i;;tj;,::ijti;;, 59 | ## ,,;;,::.,;,..:t,,;:.:,ttittDG;jG;ti...:jjj:.....:;j:ttijt;;jK#GD;;;WEf,;jGLft;,j,;;;ittjti 60 | ## i,:;;,::......t:;;:.:it;iiDEDfifL;f;:........:::j;:f;jjt,;jK##EDE,,ttjGEEKWKf;,,,, :,i;:tt 61 | ## ..,;;t::.:.:..:t,;:.:t,;;iDEDGDGDEGDL:....::.:;i:ittfii;jL##W#EjEEDKGGDGDDDDEKE:,,jL;::,;, 62 | ## ::..:;;,:..:.:.t,i,:,j,;;fDEDGGGGGGGGL:.:....,jtLi.ji;ijfG###WEEtGKDEGGDDDDDEDEG.;,tffi,,; 63 | ## ;,::.:.:.:....:;i;,:,t;;tEDEDGDDGDDGGGG;;;tj;jfj;.it,tjjt####KEDKEEGLGLGGGGDGEEK;,;tfjGti, 64 | ## ,i;:::.........:ii;:;i;;tEDEDGDDGGGGGGKEEWt,:ti;.:t;jjttD####KEEKEGLLLLLLGGGGDEEi:;i,jjfji 65 | ## ;;i;:...........iit,,;,;LDGDDGDEGDGGGDWDDK,,ii;:.j,ftit;#WW#WKEKEGLLLLLLLGGGGDEKf,i,jt;LLj 66 | ## ...............::KL;:;,iDLGDDGDDDDGGGKEEDD.,fi,.,;fi;t;jW#W#KKKEGLLLLLLLLGGGGDEEfii:L;tfff 67 | ## ,,,,::.........::;GEt:iGDGDLDLEDEDGGEEDDK:itD,:.if;;i;fW####KWDLLLLLLLLLLLLGDDEELti,fLjffj 68 | ## ,;ii,:.........:::LEGtDGGGDGDGEEEDGGEDDDD,LjD.:;ji;;;tW###WWWDLLLLLLGGGLLLGGDEEEf:;,ff;fjj 69 | ## ;,,i;,.........:::LDLGfGGGGGDGEEEDGDDDDDLffGD,:jt;;;iG##WEKKGLLLLGDDDGGLLLGDEEEKt;:,fjjjLj 70 | ## ;,,,i,::........,ifELfjLGGLGEDKEDGGGGGGGLfLGG,:L,;;ifWWEDEEGLLLGDEDGLLLLLGDDEEEDii;,ffLfLt 71 | ## :,;,;,,::::..:..:fLDfLfLLGGGDDWDDLGGGLGGfjDGf;:i::,tLEDEEDfLLGEEEDGLLLLLGGDEEEEtL,i:fLGjfi 72 | ## ,,,;,;,,:::..:::,LLD:fL;GLLDDEEGLGGLLLLjfjDfG;,,.:ifEDDEGLGEEEEGLLLLLLLLGEEEEKGfji;:ffjLtj 73 | ## :::,,,;,,::::,::GDGKG:LiLDGGDKGLLLLLLffj.GELGji;:;:DDDDGGKKEDDLLLLLLLLLGDEEEEGEf,t:ifftjLE 74 | ## :,,:,;;;,,,,i.;GDL;GKfj;GLDDEELLLfLfLLti:DDfDti;,:iDDDDGWEEGLGLLLLLLLLGDEEEEDEDji;:ffLjjEE 75 | ## ::,,:,,iGiii,jDDDG;:GK:;fLGDEDLLEfLLfjf;fDfGGLtt:,LDEEDWEDGGLLLLLLLLLGDEEDEDDEDtt,iLftfLDE 76 | ## :::,::;fDjiiEEDDELLj:LD,LfLLELLfGEfftif:GE,GGfjij;GDDEWWGGGLLLLLLLLLGEEEDEDGKEGi;;fLf;LLDE 77 | ## :::::,:EE;tWWWKEGGGfi:Df,LLLGGfffLDfij,tDf:GDjtj,G;fEEWEGGGLLLLLLLLGEEEDDELEEKLt,jGLf,GjED 78 | ## :::::,i#WWW#WWDGGG;jLL;iG:ffGLDGLLLLLG.GL,jGG;jiGKGj;EKDGGLLLLLLLLGEEGGEDtEKEWL;tDELL,LGLE 79 | ## ::::,:LW#W#WKDDGG;tGf,Gf,L;LLLGDGGGGGG,Gi,LGG;j;EDLDiGDGGLLLLLLLLDEDGGDDEGEEEWL;fDKDf;jDLG 80 | ## ::::,,LWW##EEEDDijGj;LGfG;j;fLLGDDGDGLGG:;GGGii;DLLLGjEGGLLLLLLLDEDGGDDEGKEEKKLiGEEKf;ijDG 81 | ## :::::,.GWWEEDEDtfGj;LLfGLGitLLLLGDDDtLDj:tLGGL;tGLLGEjDGLLLLLLGDEGGGDDEEjEEEKEGtDKEEfi,tiG 82 | ## ::::.::DKEEEELiGEtfGGLLLLLLf;LLLGLEE:GL,.;LDDL,GLLLDEfGLLLLLLLEDLGGDEEEEtEEEEEDfEEDGEt,it; 83 | ## ::::::,GEEEDtfDEjLDDGGGGLLLLjtLLLGDEtLG:,.fGLifLLGEEDLGGLLfLGELLGGDDEEEEtEEEEEELEDDDEG;,it 84 | ## :::::,,,;GtGEDjfEEEDDGGGLLLLfLLLLLGLDG:.:.tLfGGGGGGGGGGLLGDDGLGLGDEEEEEEfKEKKEEfEEEDGGD;i, 85 | ## :,,:,:;::iGELtGEEEEEEDGGLLGLLLLGGGLGLG:,..ttjDDDDGEGGGGGDEDLLLGGDDEEEEKEGKKKEEEfEEDGGLGL;; 86 | ## :::::::,,,itfDEEEEEEEDDGGGLLLLLLGGLLGLDi,:tjGDDDDEDGGDEEDLLLGLGGDEEEEEKKEKKEEEEDGDGGGGGDj; 87 | ## :::::,:,;,,tEDEEEEEEEEDDGGLLLLLLGGLLLGLL:,jGEEEEEEGEKEEDLLLLGGDDEEEEEEWWWEEEEDEDGGGGGGGGE, 88 | ## :::::::::,;;EDDEEEEEEEEDDGGLLLLLGGGLLLGGL;GDKKKKKDKEEEGfLLGLGGDEEEEEEKWWEGKEEDGGGGGGGGDGGi 89 | ## ::::::::,:,DDEKEEEEEEEEEEDGGGLLLGDDGLLLGGKDEWKWEKEKDLLLLLLLGGDEEEEEKWKKKEKDEDGGGGGDDDDGLLf 90 | ## :::::::::,:EEEEEEEEEEEEEEDDGGLLLGDDGLLLLLDEEEKKWKEDLLLLLLLLGDEEEEEEWKKKKEKEDGGGGGDDDDGLGDt 91 | ## ::::::::,:fGEEKEEEEEEEEEEEEDGGLLGDDDGLLLGGEEEDEGGGLLLLLLGGGGDEEEEEKKEEEKKEEGGGGGDDEDGGGEEt 92 | ## :::::::::,DEEDDEEDEEEEEEEEEEDGLLLGEDDLLLLGEEDKDKEEEEDDDGGLGDEEEEEKWEEEEKKEEGGGDEEEDGDDEEEL 93 | ## ::,::,:::jDDEDEDDDDEEEEEEEEEDGGLLGEEDGLLLLDEEEDKKGGEEEEEEGGDEEEEKWEEEKKKKEDGDEEDGGDEEKEGDG 94 | ## :::::::::DDDGEGGDGGDEEEEEEEEDDGLLLDEDGLLLLGDEDDDKEKLLGDDEGGDEEEEKKEEEKKKKEDEEEDDDEEEEGLLGD 95 | 96 | SUPERUSERS = { } 97 | SELF_QQ = 0 98 | COMMAND_START = {''} 99 | NICKNAME = {'千堂', '千堂 瑛理華', '千堂瑛理華', '千堂瑛理华', 100 | '千堂 瑛理华', '千堂瑛里华', '千堂 瑛里华', '瑛里华', '瑛理华', 'erika'} 101 | SESSION_EXPIRE_TIMEOUT = timedelta(minutes=2) 102 | 103 | HOST = '172.17.0.1' 104 | PORT = 8080 105 | 106 | DEBUG = False 107 | 108 | # PLUGIN-SPECIFIC SETTINGS 109 | 110 | AMAP_WEATHER_API_KEY = '' 111 | 112 | OPENWEATHERMAP_API_KEY = '' 113 | 114 | GLOT_RUN_TOKEN = '' 115 | -------------------------------------------------------------------------------- /doc_assets/custom_reply.md: -------------------------------------------------------------------------------- 1 | In a group chat, you might want the bot to do a "keyword reply". For example, if someone says `在吗?` the bot replies `在!`. 2 | The custom reply plugin is based on the private chat window. By chatting with the bot with commands one can easily modify the keyword settings. 3 | 4 | All matchings are stored in a json file called `group_data.json` under its module path. Matchings split into qq groups, which means these settings are group-specific. One special part is there exists a "group number" called `global`, which means keywords in this dict respond to every group. 5 | 6 | ``` 7 | ┌ "global": .... groups 123456 654321 also apply 8 | settings-┼ "123456": .... 9 | └ "654321": .... 10 | ``` 11 | 12 | In each group, there are three modes: full match (1), inclusive match (2), and regex match (3). 13 | * full: replies only when the keyword completely matches the message 14 | * inclusive: replies when keywords are *in* the message 15 | * regex: replies when the message matches given pattern 16 | 17 | ##### Creating keyword pairs 18 | Open a private message box with the box as a superuser. hit and observe 19 | ``` 20 | >> 群关键字 add global 3 21 | 在这里输入关键字 22 | >> ^[A-Z]{3,4}[-\._]?[0-9]{3,5}$ 23 | 在这里输入回复 24 | >> ナニコレ?? 25 | success! 26 | ``` 27 | In the first line, `群关键字` is the name of the command, `add` is the add mode which means you are adding keywords, `global` means you are adding stuff in the global folder so that it works in every group, `3` means the keyword works in regex-matching mode. 28 | You then typed `^[A-Z]{3,4}[-\._]?[0-9]{3,5}$`, this is a keyword. You then typed the reply, and the bot responds with a success message. The pair will take effect instantly. 29 | 30 | Thus when any group member's message matches this pattern, like `AAA1111`, the bot replies with `ナニコレ??`. 31 | 32 | Or it might say `?`, this is because this keyword already exists in the factory configure file, adding an existing keyword is in fact appending: two replies both exist and a randomly chosen one will be sent. This is why in the configure the reply is in the form of a list. 33 | 34 | ##### Removing keyword pairs 35 | Note the deleting mode will delete *all* replies paired with the given keyword. 36 | ``` 37 | >> 群关键字 del global 3 38 | 在这里输入关键字 39 | >> ^[A-Z]{3,4}[-\\._]?[0-9]{3,5}$ 40 | success! 41 | ``` 42 | The match we just set was then deleted. In other occasions, if you typed a keyword that does not exist, an error will be reported. 43 | 44 | Note, instead, you can actually delete all pairs in one group at once: 45 | ``` 46 | >> 群关键字 delall 123456 47 | success! 48 | ``` 49 | 50 | ##### Editing keyword pairs directly from the json file 51 | The json file come along has its pre-setup reply pairs which will guide you the proper format to edit them. 52 | 53 | ##### Variable as reply 54 | In any reply, you can insert variables. To plug a variable inside the content, just type these in place of the variable: 55 | * `{SENDER_ID]` sender's qq 56 | * `{SENDER_NICK]` sender's nickname 57 | * `{SENDER_CARD]` sender's namecard 58 | * `{SENDER_ROLE]` sender's role (member or admin) 59 | * `{SENDER_TITLE]` sender's title 60 | 61 | The availability of them depends on the context. 62 | 63 | ##### Checking keyword pairs 64 | This command will list the first 10 replies of *each* keyword match 65 | ``` 66 | >> 群关键字 view global 3 67 | global, regex_match 68 | "^[A-Z]{3,4}[-\._]?[0-9]{3,5}$": "?"; "ナニコレ??" 69 | ``` -------------------------------------------------------------------------------- /doc_assets/evaler.md: -------------------------------------------------------------------------------- 1 | This feature is provided by https://glot.io (whose github webpage is https://github.com/prasmussen/glot-run/) and its associated api. Example: 2 | ```c# 3 | >> exe c# 4 | class MainClass { 5 | static void Main() { 6 | System.Console.WriteLine("Hello World!"); 7 | } 8 | } 9 | -------------------------------------- 10 | stdout: Hello World! 11 | stderr: 12 | error: 13 | ``` 14 | 15 | You must visit the page above to create a free account and then go to https://glot.io/account/token to obtain an api key. fill in the key in the `GLOT_RUN_TOKEN` variable in the file `config_bot.py`. -------------------------------------------------------------------------------- /doc_assets/notifyme.md: -------------------------------------------------------------------------------- 1 | You can talk to the bot to schedule a timer, when the timer expires, the bot will get back to you 2 | 3 | ``` 4 | [11:00:00] > 千堂定时提醒 5 30 吃饭 5 | [11:00:00] 已设定: 6 | 05月06日 16:30 吃饭 7 | ... 8 | [16:30:00] @你 吃饭 9 | ``` 10 | 11 | Partial natural language is supported. example: 12 | 13 | ``` 14 | [11:00:00] > 千堂5小时30分后提醒我吃饭 15 | [11:00:00] 已设定: 16 | 05月06日 16:30 吃饭 17 | [11:00:20] > 千堂取消定时提醒 18 | [11:00:20] 已取消定时提醒 19 | ``` 20 | 21 | limitation: 22 | 23 | currently the scheduled job store is not persistent, which means after rebooting the bot all scheduled jobs will be lost. 24 | -------------------------------------------------------------------------------- /doc_assets/remotely.md: -------------------------------------------------------------------------------- 1 | This plugin is simple. By chatting with the bot as a superuser with 2 | ``` 3 | 发送到QQ 12345678 hello, world 4 | ``` 5 | You sent a message to the user. You can also send messages to groups by using 6 | ``` 7 | 发送到群 12345678 hello, world 8 | ``` 9 | Using following you can embed CQ codes: 10 | ``` 11 | 发送到QQCQ 876543210 [CQ:face,id=14] 12 | 发送到群CQ 12345678 [CQ:at,qq=876543210] 13 | ``` -------------------------------------------------------------------------------- /doc_assets/repeater.md: -------------------------------------------------------------------------------- 1 | By default, when a same message in *one* group repeats 3 times, the bot will speak the same sentence once and only once. You can change this number by inspecting `__init__.py` under `/repeater`, the third line from the bottom. -------------------------------------------------------------------------------- /doc_assets/signin.md: -------------------------------------------------------------------------------- 1 | involke in a group chat like this: 2 | ``` 3 | > 千堂签到 4 | @你 早安! 好感度:1.8 (+0.4) 5 | > 千堂签到 6 | 今日签到过啦~ 好感度:1.8 7 | > 千堂签到信息 8 | 好感度:1.8 9 | 历史签到次数:5 10 | 上次签到:2019.08.27 11 | > 千堂签到排名 12 | ... 13 | ``` 14 | 15 | This plugin overlaps with test_rp plugin. Please remove it to use this. 16 | 17 | The sign_in plugin provides basic daily signing feature. It is a group-based feature. User in different groups can sign in and gain points. 18 | 19 | The data is stored in a sqlite database and is managed by `SignInSession` class. The structure of the signin data is dipicted as follows 20 | 21 | ``` 22 | table name: sign 23 | 24 | fields: 25 | identity (format: userid_groupid)(str), 26 | last sign in time (str), 27 | score (int), 28 | luck (not used)(str), 29 | score2 (stores the sign in count)(int), 30 | score3 (not used)(int) 31 | ``` 32 | 33 | There are some fields that are not used. Feel free to implement. 34 | 35 | A row fetch looks like this: 36 | ```py 37 | ('12345678_7777777', '27082019', 180, '', 5, 0) 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /doc_assets/start.md: -------------------------------------------------------------------------------- 1 | ### START 2 | 3 | open config_bot.py and modify: 4 | * `SUPERUSERS: dict[int]` This contains all qq IDs that have full control to the bot, type yours here. 5 | * `SELF_QQ: int` Bot's qq. 6 | * `COMMAND_START: dict[str]` the prefix to call the bot. If you have an empty string `''` this means no prefix will be used. 7 | * `NICKNAME: dict[str]` Applies only in a group chat, except special settings, the bot will respond only to those messages which start with an `@` or this nickname, for example: 8 | ```python 9 | 千堂查天气 new york # responds 10 | 查天气 new york # does not respond 11 | ``` 12 | * Change `HOST` and `PORT` as appropriate; because CoolQ http plugin will open a server to process requests, you plug in its location. Refer to https://cqhttp.cc/docs/4.10/#/ if you do not know. 13 | 14 | * run `python3 bot.py` and the bot is working. If there are exceptions prompting you to install modules, do so. 15 | 16 | Open a private message window with the bot, and hit: 17 | ``` 18 | 发送到QQ 12345678 hello, world 19 | ``` 20 | Where you know where your qq goes (12345678). Since it does not matter in private chats whether you call the nickname, you will see: 21 | ``` 22 | hello, world 23 | success! 24 | ``` 25 | The first message indicates you controlled the bot to send a message to you: "hello, world", the other says it is successful. Sometimes if you do not see the success! message, there might be some problems. 26 | 27 | Now the bot is working properly. But to use more features, read the following docs... 28 | 29 | Or you might want to type `help` to the bot. -------------------------------------------------------------------------------- /doc_assets/weather.md: -------------------------------------------------------------------------------- 1 | This plugin currently supports two data sources: 高德 (Amap) and OpenWeatherMap. Amap will fetch the weather data for cities in China, and OWM works for anywhere outside (in English) 2 | 3 | A sample Chinese city search looks like: 4 | ``` 5 | >> 千堂 天气 香港 6 | -------------------------------------- 7 | 预报时间:2019-05-29 22:51:39 8 | 地区:香港 香港特别行政区 9 | 2019-05-29 日:27°C,阴,无风向 10 | 2019-05-29 夜:24°C,阵雨,无风向 11 | ...... 12 | ``` 13 | Where `千堂` is my nickname. This 3-day forecast result is given by Amap. While if an English name is passed, then the result looks like: 14 | ``` 15 | >> 千堂 天气 montreal 16 | -------------------------------------- 17 | region: MONTREAL, CA, time diff from UTC: -4.0h 18 | report time: 2019-05-29 18:00:00 19 | 2019-05-29 18:00: Clouds (overcast clouds), 16.44-17.91°C 20 | wind: 3.96m/s (222.787°), humidity: 65, pressure: 1004.83hPa 21 | 2019-05-30 06:00: Clouds (broken clouds), 10.46°C 22 | wind: 2.46m/s (266.48°), humidity: 95, pressure: 1005.19hPa 23 | ...... 24 | ``` 25 | This is given by OWM. But if you pass a Chinese string which does not correspond to a Chinese city, the translator module is activated (1000 times per day, as documented): 26 | ``` 27 | >> 千堂 天气 蒙特利尔 28 | -------------------------------------- 29 | region: MONTREAL, CA, time diff from UTC: -4.0h 30 | ...... 31 | ``` 32 | 33 | 34 | To use this, you must request their free api keys separately. 35 | 36 | For 高德 (Amap), go to https://lbs.amap.com/api/webservice/guide/api/weatherinfo/ and paste the key in the `AMAP_WEATHER_API_KEY` variable in `config_bot.py`. 37 | 38 | For OWM, go to https://openweathermap.org/forecast5 and paste the key in the `OPENWEATHERMAP_API_KEY` variable in `config_bot.py`. -------------------------------------------------------------------------------- /plugins/_googlesearch/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import CommandSession, on_command 2 | from nonebot.permission import * 3 | 4 | from utils_bot.command_ops import global_cooldown 5 | from utils_bot.logging import logger 6 | from utils_bot.typing import depreciated 7 | 8 | from .data_source import getGoogling 9 | 10 | __plugin_name__ = 'google' 11 | __plugin_usage__ = r'''feature: google search 12 | 13 | google 获取前3条 google search 结果 14 | 15 | DEPRECIATED 16 | ''' 17 | 18 | 19 | @on_command('google', permission=SUPERUSER | GROUP_MEMBER) 20 | @global_cooldown(40) 21 | @depreciated 22 | async def google(session: CommandSession): 23 | try: 24 | keyword = session.get('keyword') 25 | report = await getGoogling(keyword) 26 | await session.send(report) 27 | logger.info(f'google search called: {report[:37]}...') 28 | except ValueError: 29 | await session.send('error') 30 | 31 | @google.args_parser 32 | @depreciated 33 | async def _(session: CommandSession): 34 | paramStr = session.current_arg_text 35 | # if arg list is not empty 36 | if paramStr: 37 | session.state['keyword'] = paramStr.strip() 38 | else: 39 | session.finish(__plugin_usage__) 40 | -------------------------------------------------------------------------------- /plugins/_googlesearch/data_source.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from aiohttp import request 4 | import bs4 5 | 6 | from utils_bot.typing import depreciated 7 | 8 | # 052219: adding browser headers creates cache in resulting page 9 | _headers = { 10 | 'Cache-Control': 'private, max-age=0, no-cache', 11 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 12 | 'Accept-Language': 'en-US,en;q=0.5', 13 | 'Accept-Encoding': 'gzip, deflate', 14 | 'DNT': '1', 15 | 'Connection': 'keep-alive', 16 | 'Upgrade-Insecure-Requests': '1' } 17 | _browser = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0' } 18 | 19 | @depreciated 20 | async def getGoogling(*keyword) -> str: 21 | header = _headers 22 | res = '>_< ' 23 | try: 24 | count = 0 25 | linkElems, greenElems = [], [] 26 | while len(linkElems) == 0: 27 | async with request( 28 | 'GET', 'https://google.com/search?q=' + keyword[0], 29 | headers=header) as resPage: 30 | soup = bs4.BeautifulSoup(await resPage.text(), features='lxml') 31 | 32 | # linkElems contains titles and GOOGLE URLS (without domain prefix) 33 | linkElems = soup.select('.r a') 34 | # greenElems contains displaying urls 35 | greenElems = soup.select('cite') 36 | count += 1 37 | if count == 10: 38 | header = dict(_headers, **_browser) 39 | elif count == 12: 40 | raise Exception('Google 好像此时不想让我们连接') 41 | 42 | displayLen = 3 43 | 44 | linkElems = linkElems[:3] 45 | greenElems = greenElems[:3] 46 | 47 | except Exception as exc: 48 | return str(exc) 49 | 50 | def parseOneGoogleUrl(gUrl) -> str: 51 | return re.search(r'q=(.+?\..+?)&[a-z]+=[A-Z]', gUrl).group(1) 52 | 53 | for j in range(displayLen): 54 | try: 55 | realUrl = parseOneGoogleUrl(linkElems[j].get('href')) 56 | except Exception: 57 | realUrl = greenElems[j].getText() 58 | res += linkElems[j].getText() + '\n' + realUrl + '\n' 59 | 60 | return res 61 | -------------------------------------------------------------------------------- /plugins/_sign_in/__init__.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from os import path 3 | 4 | from nonebot import CommandSession, get_bot, on_command 5 | from nonebot.permission import * 6 | from aiocqhttp.exceptions import ActionFailed 7 | 8 | from utils_bot.logging import logger 9 | from utils_bot.typing import Tuple 10 | 11 | from .execute import SignInSession, format_score, generate_luck_result 12 | from .greets import get_greeting 13 | 14 | ## this plugin overlaps with test_rp plugin 15 | ## 和 test_rp (今日运气)功能重合,欲启用此请先停用 test_rp 16 | 17 | __plugin_name__ = '签到/运气' 18 | __plugin_usage__ = f'''feature: 要不要看一看自己的人品? 19 | 功能:签到,签到信息,今日运气,签到排名 20 | ''' 21 | 22 | 23 | bot = get_bot() 24 | 25 | SIGN_IN_DB_PATH = path.join(path.dirname(__file__), 'signin.db') 26 | 27 | # initialize db obsolete 28 | @bot.server_app.before_serving 29 | async def initialize_db(): 30 | async with SignInSession(SIGN_IN_DB_PATH, 0, 0) as table_init: 31 | logger.info('loading signin db...') 32 | await table_init.init_table() 33 | 34 | 35 | def get_user_and_group_ids(session: CommandSession) -> Tuple[int, int]: 36 | user_id: int = session.event['user_id'] 37 | group_id: int = session.event['group_id'] 38 | return user_id, group_id 39 | 40 | 41 | @on_command('签到', aliases=('sign',), permission=GROUP_MEMBER) 42 | async def sign_in(session: CommandSession): 43 | user_id, group_id = get_user_and_group_ids(session) 44 | async with SignInSession(SIGN_IN_DB_PATH, 45 | user_id, group_id) as signin_session: 46 | await signin_session.init_user() 47 | status, score, added = await signin_session.user_sign_in() 48 | if status: 49 | await session.send( 50 | f'{get_greeting(score)} 好感度:{format_score(score)} (+{added})', 51 | at_sender=True) 52 | else: 53 | await session.send( 54 | f'今日签到过啦~ 好感度:{format_score(score)}', 55 | at_sender=True) 56 | 57 | logger.info(f'{user_id} trying to sign in in group {group_id} success: {status}') 58 | 59 | 60 | @on_command('签到信息', aliases=('信息', 'signinfo', '好感度'), permission=GROUP_MEMBER) 61 | async def check_sign_in_info(session: CommandSession): 62 | user_id, group_id = get_user_and_group_ids(session) 63 | async with SignInSession(SIGN_IN_DB_PATH, 64 | user_id, group_id) as signin_session: 65 | await signin_session.init_user() 66 | score, count, last = await signin_session.user_check() 67 | await session.send( 68 | f'\n好感度:{format_score(score)}\n历史签到次数:{count}\n上次签到:{last[4:]}.{last[:2]}.{last[2:4]}', 69 | at_sender=True) 70 | 71 | 72 | @on_command('我的运气', aliases=('运气', '今日运气', '今日人品', '我的人品', 'jrrp', '运势', '今日运势'), permission=GROUP_MEMBER | SUPERUSER) 73 | async def my_luck_today(session: CommandSession): 74 | senderId: int = session.event['user_id'] 75 | await session.send(generate_luck_result(senderId), at_sender=True) 76 | 77 | 78 | @on_command('签到排名', aliases=('好感度排名'), permission=GROUP_MEMBER) 79 | async def signin_ranking(session: CommandSession): 80 | group_id: int = session.event['group_id'] 81 | async with SignInSession(SIGN_IN_DB_PATH, 0, group_id) as signin_session: 82 | ranking = await signin_session.group_top_five() 83 | 84 | async def line(row): 85 | user_id, score = row 86 | try: 87 | name = (await bot.get_group_member_info(group_id=group_id, user_id=user_id))['card'] \ 88 | or (await bot.get_stranger_info(user_id=user_id))['nickname'] 89 | except ActionFailed: 90 | # user left group 91 | name = user_id 92 | return f'{format_score(score).ljust(8)}: {name}' 93 | 94 | result = await asyncio.gather(*(line(row) for row in ranking)) 95 | await session.send('好感度排名:\n' + '\n'.join(result)) 96 | -------------------------------------------------------------------------------- /plugins/_sign_in/execute.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing import List 3 | 4 | import aiosqlite 5 | 6 | from utils_bot.datetime import TZ, datetime 7 | from utils_bot.typing import Tuple, Union 8 | 9 | 10 | def today_to_str() -> str: 11 | return str( 12 | datetime.now(TZ).strftime('%m%d%Y') 13 | ) 14 | 15 | 16 | def generate_luck_num(sender_id: int) -> float: 17 | 'a user has a luck number between 0 and 1 every day' 18 | senderId: int = sender_id 19 | timeStamp: int = int(today_to_str()) 20 | seed: int = (senderId * 2) | (timeStamp * 333) 21 | random.seed(seed) 22 | res: float = random.random() 23 | random.seed() 24 | return res 25 | 26 | 27 | def generate_luck_result(sender_id: int) -> str: 28 | 'converts the luck number of a user to a suitable string' 29 | def return_luck_by_num(num: float) -> str: 30 | if 0.0 <= num < 0.1: 31 | return '极坏' 32 | elif 0.1 <= num < 0.4: 33 | return '坏' 34 | elif 0.4 <= num < 0.7: 35 | return '一般' 36 | elif 0.7 <= num < 0.9: 37 | return '好' 38 | else: 39 | return '极好' 40 | return return_luck_by_num(generate_luck_num(sender_id)) 41 | 42 | 43 | def user_score_to_user(i: int) -> float: 44 | "a user's score is stored in int, but the displayed value is that divided by 100 (by now)" 45 | return i / 100 46 | 47 | 48 | def format_score(s: float) -> str: 49 | 'format the displayed score (好感度) by hearts' 50 | if s < 100: 51 | return str(s) 52 | else: 53 | # example: '4.44 ♥' means 104.44 54 | return f'{float(s % 100):.4} {"♥" * int(s // 100)}' 55 | 56 | 57 | class SignInSession: 58 | 59 | ''' 60 | Table attributes: 61 | 62 | table name: sign 63 | 64 | identity (format: userid_groupid)(str), 65 | last sign in time (str), 66 | score (int), 67 | luck (not used)(str), 68 | score2 (stores the sign in count)(int), 69 | score3 (not used)(int) 70 | ''' 71 | 72 | def __init__(self, db_name: str, 73 | user_id: Union[str, int], 74 | group_id: Union[str, int]): 75 | 76 | self.conn = aiosqlite.connect(db_name) 77 | self.user_id: int = int(user_id) 78 | self.group_id = int(group_id) 79 | 80 | @property 81 | def identity(self) -> str: 82 | return f'{self.user_id}_{self.group_id}' 83 | 84 | async def searchall(self) -> list: 85 | cur = await self.conn.execute('select * from sign') 86 | res: list = await cur.fetchall() # type: ignore 87 | await cur.close() 88 | return res 89 | 90 | async def init_table(self): 91 | cur = await self.conn.execute( 92 | '''CREATE TABLE IF NOT EXISTS sign 93 | (identity VARCHAR(30) PRIMARY KEY, 94 | lastsign VARCHAR(10), 95 | score INT, 96 | luck VARCHAR(10), 97 | score2 INT, 98 | score3 INT)''' 99 | ) 100 | await cur.close() 101 | await self.conn.commit() 102 | 103 | async def init_user(self): 104 | 'create one entry for a user who wants to sign in' 105 | cur = await self.conn.execute('''INSERT OR IGNORE INTO sign 106 | (identity, lastsign, score, luck, score2, score3) 107 | values (?, ?, ?, ?, ?, ?)''', 108 | (self.identity, '0', 0, '', 0, 0)) 109 | await cur.close() 110 | await self.conn.commit() 111 | 112 | async def user_sign_in(self) -> Tuple[bool, float, float]: 113 | '''after user initializing, signs in. one user signs in once a day. 114 | returns the successfulness of the signing in. returns the score after signing in. returns the score added 115 | ''' 116 | 117 | today: str = today_to_str() 118 | async with self.conn.execute('SELECT * FROM sign WHERE identity=?', (self.identity,)) as cur: 119 | currentEntry = await cur.fetchone() 120 | scoreBefore: int = currentEntry[2] 121 | # checks sign in time, refuses if already signed in 122 | if currentEntry[1] == today: 123 | return False, user_score_to_user(scoreBefore), 0 124 | 125 | scoreAdded: int = int(generate_luck_num(self.user_id) * 100) 126 | scoreAfter: int = scoreBefore + scoreAdded 127 | await cur.execute('UPDATE sign SET score=?, lastsign=?, score2=score2+1 WHERE identity=?', 128 | (scoreAfter, today, self.identity)) 129 | await self.conn.commit() 130 | return True, user_score_to_user(scoreAfter), user_score_to_user(scoreAdded) 131 | 132 | async def user_check(self) -> Tuple[float, int, str]: 133 | 'returns the score, sign-in count, last sign-in time for the user' 134 | async with self.conn.execute('SELECT * FROM sign WHERE identity=?', (self.identity,)) as cur: 135 | currentEntry = await cur.fetchone() 136 | return user_score_to_user(currentEntry[2]), currentEntry[4], currentEntry[1] 137 | 138 | async def group_top_five(self) -> List[Tuple[int, float]]: 139 | 'returns list of users\'s qq followed by score, ordered descending' 140 | res = await self.conn.execute_fetchall('''SELECT identity, score FROM sign 141 | WHERE identity LIKE ? 142 | ORDER BY score DESC 143 | LIMIT 5''', 144 | (f'_{self.group_id}',)) 145 | return [(int(row[0].split('_')[0]), user_score_to_user(row[1])) for row in res] 146 | 147 | async def __aenter__(self) -> 'SignInSession': 148 | await self.conn 149 | return self 150 | 151 | async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: 152 | await self.conn.close() 153 | -------------------------------------------------------------------------------- /plugins/_sign_in/greets.py: -------------------------------------------------------------------------------- 1 | from random import choice 2 | 3 | GRP_1 = [ 4 | '我是学院中最有人气的女生。', 5 | '要想获得本大小姐的青睐,可不是件容易的事噢?', 6 | '我——是——大——小——姐!', 7 | '请挺起胸膛、在这里我们才是主角!', 8 | '你知不知道、好奇心会杀死猫这句话?', 9 | '不过算了。因为我现在心情很好。', 10 | '啊……嗯?', 11 | '太邋遢了哦', 12 | '早安!' 13 | ] 14 | 15 | GRP_2 = [ 16 | '呐,到底是哪里?', 17 | '如果会后悔的话,就不会来这里了。', 18 | '那也太凄凉了吧。我会花些心思的。', 19 | '因为没办法才照顾你的哟,麻烦好好地感谢我一下!', 20 | '麻烦好好地照顾我一下!', 21 | '一,一大早的说什么呢你!', 22 | '谢,谢……', 23 | '星空好美啊', 24 | '为什么在发抖?', 25 | '听说城市里都看不到星星呢', 26 | '人们会有点寂寞吧,那样子……', 27 | '欢迎回来' 28 | ] 29 | 30 | GRP_3 = [ 31 | '好了,一鼓作气地上吧!', 32 | '这么令人愉快的早晨,还是第一次', 33 | '海水浴?', 34 | '没事的,我已经镇定下来了', 35 | '对了,来喝杯茶吗?', 36 | '我也是第一次有那种感觉,连自己都吓了一跳', 37 | '诶……是,是吗', 38 | '嗯,真让人期待', 39 | '马上就到讲堂了,加油啊', 40 | '啦~啦啦~~', 41 | '我哪里像反派了?', 42 | '不·要·生·气', 43 | '好,好了,再不快点真的会迟到的哦', 44 | '什么嘛,这不是走得挺快的么', 45 | '了解。稍等一下哦', 46 | '冰柠檬茶,这里做的可是非常好哦', 47 | '抱歉。我喝不来苦的东西', 48 | '话说回来,今天,你有时间么', 49 | '太好了,希望你能陪我一下', 50 | '不用在意,这也是自然的', 51 | '呀啊!? 啊?哎?', 52 | '唉,又来了么', 53 | '话说你又如何?有女朋友么?你的素质可是很高的,要加油哦' 54 | ] 55 | 56 | GRP_4 = [ 57 | '在这大家庭里面,你不感到幸福吗?', 58 | '呐,以后打算怎么办啊?', 59 | '总感觉你说了很帅气的话', 60 | '你明白的吧?', 61 | '呜啊啊啊啊啊啊啊!!', 62 | '啊,是,是呢', 63 | '自作自受,偷窥可不是什么好习惯', 64 | '不要太捉弄我啊', 65 | '刚刚还夸下海口,说什么【可以尽可能把自己心中的烦恼对他人倾诉了】 我真是……虚伪呢', 66 | '拿你没办法', 67 | '被这样子抚摸着,总觉得好安心', 68 | '我没有想和谁交往的想法,之前不是说过了么', 69 | '不要戏弄我啊!', 70 | '呵呵,说不定是这样的呢' 71 | ] 72 | 73 | GRP_5 = [ 74 | '你,你要让我说什么啊?', 75 | '…………我们的关系,算是那个……了吗?', 76 | '为什么我的心要跳得这么快啊,为什么离开你就会寂寞,为什么你总是令我不知所措。你这该死的笨蛋,为什么总是来招惹我啊!', 77 | '至少,我能确信自己的这份感情了', 78 | '嗯。让对方了解到自己时的开心,跟血的欲求是没有关系的吧?', 79 | '能让对方了解自己,不是很值得开心的事情吗', 80 | '我,我喜欢……', 81 | '我,我喜欢……呜啊啊啊啊啊啊啊!!', 82 | '为,为什么那么高兴啊', 83 | '嗯,我也是,请多关照', 84 | '对不起,你明明是一片好心', 85 | 'kiss~', 86 | '要好好闭上眼才行的' 87 | ] 88 | 89 | GRP_6 = [ 90 | '就会开始想一些下流的事情?', 91 | '早上好老公……开玩笑的', 92 | '啊,嗯,嗯', 93 | '我也这么想的,那样的也还是第一次呢。简直就像是在吵架一样', 94 | '如果那种方式是常态反而伤脑筋了……', 95 | '又在想什么色色的事情了吧?', 96 | '我也会不好意思的啊,稍微称赞一下也好嘛', 97 | '只是想戏弄你一下而已', 98 | '嗬~', 99 | '那,一会儿见', 100 | '我还是第一次去海边玩', 101 | '说,你喜欢我', 102 | '啊……稍,稍微……等一下', 103 | '我会好好地照料你的', 104 | '那么,我给你做饭', 105 | '结果一整天都在忙个不停啊', 106 | '感觉,好幸福呢', 107 | '结婚,是么……', 108 | '真是的……笨蛋', 109 | '第一次相遇是在这里呢', 110 | '那边会不会也是樱花盛开的时节呢?', 111 | '啾……嗯……嗯啊,嗯' 112 | ] 113 | 114 | def get_greeting(score: float) -> str: 115 | if 0 <= score < 5: 116 | return choice(GRP_1) 117 | elif 5 <= score < 20: 118 | return choice(GRP_1 + GRP_2) 119 | elif 20 <= score < 50: 120 | return choice(GRP_2 + GRP_3) 121 | elif 50 <= score < 80: 122 | return choice(GRP_3 + GRP_4) 123 | elif 80 <= score < 100: 124 | return choice(GRP_4 + GRP_5) 125 | elif 100 <= score: 126 | return choice(GRP_5 + GRP_6) 127 | raise Exception('invalid affinity') 128 | -------------------------------------------------------------------------------- /plugins/azurlane_buildship_simulator/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command, CommandSession 2 | from nonebot.permission import * 3 | from .simulator import * 4 | 5 | __plugin_name__ = '碧蓝航线建造模拟' 6 | __plugin_usage__ = r'''feature: 碧蓝航线建造模拟 7 | 可用命令: 8 | 轻型舰建造,重型舰建造,特型舰建造 9 | 参数: 10 | [次数]次 不超过10次 11 | attribution @ 碧蓝航线wiki 12 | ''' 13 | 14 | @on_command('碧蓝航线建造模拟', aliases=('碧蓝航线建造'), permission=GROUP_MEMBER | SUPERUSER) 15 | async def build_help(session: CommandSession): 16 | await session.send(__plugin_usage__) 17 | 18 | @on_command('轻型舰建造', permission=GROUP_MEMBER | SUPERUSER) 19 | async def light_build_host(session: CommandSession): 20 | times = session.get('times') 21 | await session.send(mass_ship_build_light(times)) 22 | 23 | @on_command('重型舰建造', permission=GROUP_MEMBER | SUPERUSER) 24 | async def heavy_build_host(session: CommandSession): 25 | times = session.get('times') 26 | await session.send(mass_ship_build_heavy(times)) 27 | 28 | @on_command('特型舰建造', permission=GROUP_MEMBER | SUPERUSER) 29 | async def aircraft_build_host(session: CommandSession): 30 | times = session.get('times') 31 | await session.send(mass_ship_build_aircraft(times)) 32 | 33 | @light_build_host.args_parser 34 | @heavy_build_host.args_parser 35 | @aircraft_build_host.args_parser 36 | async def build_command_arg_parse(session: CommandSession): 37 | argsStripped = session.current_arg_text.strip(' \n次') 38 | 39 | if argsStripped: 40 | try: 41 | times = int(argsStripped) 42 | assert 0 < times < 11 43 | session.state['times'] = times 44 | except ValueError: 45 | session.finish('?') 46 | except AssertionError: 47 | session.finish('次数太多啦~') 48 | else: 49 | session.state['times'] = 10 #default 50 | -------------------------------------------------------------------------------- /plugins/azurlane_buildship_simulator/constants.py: -------------------------------------------------------------------------------- 1 | # source of probabilities: 2 | # https://wiki.biligame.com/blhx/%E5%BB%BA%E9%80%A0%E6%A8%A1%E6%8B%9F%E5%99%A8 3 | 4 | SHIP_LIST = { 5 | 'light': { 6 | 'super_rare': ['圣地亚哥', '蒙彼利埃', '贝尔法斯特', '天狼星', '确捷', '雪风', '明石', 'Z46', '阿芙乐尔', '凯旋', '恶毒', '江风'], 7 | 'elite': ['莫里', '拉菲', '圣路易斯', '小海伦娜', '丹佛', '小克利夫兰', '标枪', '无敌', '欧若拉', '谢菲尔德', '小贝法', '黑太子', '吹雪', '绫波', '野分', '夕张', '最上', '三隈', 'Z23', 'Z25', 'Z35', '长春', '太原', '逸仙', '宁海', '平海', '鲁莽', '倔强', '春月', '宵月', '花月', '长波'], 8 | 'rare': ['哈曼', '弗莱彻', '贝奇', '斯坦利', '布什', '黑泽伍德', '斯莫利', '霍比', '科尔克', '康克德', '孟菲斯', '布鲁克林', '菲尼克斯', '亚特兰大', '朱诺', '女将', '阿卡司塔', '热心', '丘比特', '泽西', '库拉索', '杓鹬', '阿基里斯', '阿贾克斯', '南安普顿', '格拉斯哥', '牙买加', '神风', '松风', '旗风', '长月', '初春', '若叶', '初霜', '有明', '夕暮', '大潮', '荒潮', '浦风', '矶风', '谷风', 'Z18', 'Z19', '清波', '莱比锡', '福尔班', '勒马尔', '文月', '朝潮', '滨风', '那珂'], 9 | 'normal': ['卡辛', '唐斯', '克雷文', '麦考尔', '富特', '斯彭斯', '奥利克', '奥马哈', '罗利', '小猎兔犬', '大斗犬', '彗星', '新月', '小天鹅', '狐提', '利安得', '睦月', '如月', '卯月', '长良', '柯尼斯堡', '卡尔斯鲁厄', '科隆'] 10 | }, 11 | 'heavy': { 12 | 'super_rare': ['明尼阿波利斯', '北卡罗来纳', '华盛顿', '胡德', '厌战', '威尔士亲王', '约克公爵', '高雄', '爱宕', '欧根亲王', '提尔比茨', '让·巴尔', '马萨诸塞', '长门', '三笠', '天城', '加贺BB', '土佐'], 13 | 'elite': ['休斯敦', '印第安纳波利斯', '威奇塔', '亚利桑那', '科罗拉多', '马里兰', '伦敦', '多塞特郡', '约克', '埃克塞特', '足柄', '声望', '伊丽莎白女王', '纳尔逊', '罗德尼', '黑暗界', '恐怖', '阿贝克隆比', '雾岛', '德意志', '斯佩伯爵海军上将', '希佩尔海军上将', '小比叡', '敦刻尔克', '铃谷', '比叡'], 14 | 'rare': ['北安普敦', '芝加哥', '波特兰', '宾夕法尼亚', '田纳西', '加利福尼亚', '什罗普郡', '苏塞克斯', '肯特', '萨福克', '诺福克', '反击', '伊势', '日向'], 15 | 'normal': ['彭萨科拉', '内华达', '俄克拉荷马', '青叶', '衣笠'] 16 | }, 17 | 'aircraft': { 18 | 'super_rare': ['企业', '埃塞克斯', '半人马', '胜利', '光辉', '翔鹤', '瑞鹤', '大凤', '伊19', '明石', 'U-81', '齐柏林伯爵', 'U-47', 'U-101', '伊168', '香格里拉', '伊13'], 19 | 'elite': ['休斯敦', '印第安纳波利斯', '威奇塔', '列克星敦', '萨拉托加', '约克城', '大黄蜂', '鲦鱼', '女灶神', '伦敦', '多塞特郡', '独角兽', '追赶者', '皇家方舟', '光荣', '伊26', '伊58', 'U-557', '小赤城', '小齐柏林', '絮库夫', '伊25', 'U-522', '伊56'], 20 | 'rare': ['北安普敦', '芝加哥', '波特兰', '长岛', '什罗普郡', '肯特', '萨福克', '诺福克'], 21 | 'normal': ['彭萨科拉', '博格', '兰利', '突击者', '竞技神'] 22 | } 23 | } 24 | 25 | # below are CUMULATIVE probabilities out of 100 26 | # order: super_rare, elite, rare, normal 27 | SHIP_CUML_PROB = { 28 | 'light': [7, 19, 45, 100], 29 | 'heavy': [7, 19, 70, 100], 30 | 'aircraft': [7, 19, 70, 100] 31 | } 32 | -------------------------------------------------------------------------------- /plugins/azurlane_buildship_simulator/simulator.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing import Dict, List 3 | 4 | from .constants import * 5 | 6 | __all__ = ['mass_ship_build_light', 7 | 'mass_ship_build_heavy', 8 | 'mass_ship_build_aircraft'] 9 | 10 | 11 | def mass_ship_build(build_list: Dict[str, List[str]], 12 | cuml: List[int], 13 | times: int) -> str: 14 | '''probabilities is a tuple(list) of four (last one is always 100) integers, 15 | corresponding to the official game''' 16 | resList = [] 17 | 18 | for _ in range(times): 19 | randomed: int = random.randint(1, 100) 20 | if 1 <= randomed <= cuml[0]: 21 | resList.append( 22 | '*SSS* ' + random.choice(build_list['super_rare'])) 23 | elif cuml[0] < randomed <= cuml[1]: 24 | resList.append( 25 | '*S* ' + random.choice(build_list['elite'])) 26 | elif cuml[1] < randomed <= cuml[2]: 27 | resList.append( 28 | random.choice(build_list['rare'])) 29 | else: 30 | resList.append( 31 | random.choice(build_list['normal'])) 32 | return '\n'.join(resList) 33 | 34 | def mass_ship_build_light(times: int) -> str: 35 | return mass_ship_build(SHIP_LIST['light'], SHIP_CUML_PROB['light'], times) 36 | 37 | def mass_ship_build_heavy(times: int) -> str: 38 | return mass_ship_build(SHIP_LIST['heavy'], SHIP_CUML_PROB['heavy'], times) 39 | 40 | def mass_ship_build_aircraft(times: int) -> str: 41 | return mass_ship_build(SHIP_LIST['aircraft'], SHIP_CUML_PROB['aircraft'], times) 42 | -------------------------------------------------------------------------------- /plugins/baidutrend/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import CommandSession, on_command 2 | from nonebot.permission import * 3 | 4 | from utils_bot.command_ops import global_cooldown 5 | from utils_bot.logging import logger 6 | 7 | from .data_source import get_baidu_trend 8 | 9 | __plugin_name__ = '百度热搜' 10 | __plugin_usage__ = r'''feature: 百度热搜获取 11 | 12 | 百度热搜 获取前6条热搜 13 | 参数: 14 | all 获取全部热搜 15 | ''' 16 | 17 | 18 | @on_command('百度热搜', aliases=('百度热点', '时事新闻'), permission=SUPERUSER | GROUP_MEMBER) 19 | @global_cooldown(40) 20 | async def trend(session: CommandSession): 21 | arg = session.get('arg') 22 | trendReport = await get_baidu_trend(arg) # arg passed to function get_baidu_trend 23 | await session.send(trendReport) 24 | logger.info(f'Baidu trend called: {trendReport[32:37]}...') 25 | 26 | @trend.args_parser 27 | async def _(session: CommandSession): 28 | paramStr = session.current_arg_text 29 | # if arg list is not empty 30 | if paramStr: 31 | session.state['arg'] = paramStr.strip() 32 | else: 33 | session.state['arg'] = '' 34 | -------------------------------------------------------------------------------- /plugins/baidutrend/data_source.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | import bs4 # and lxml 3 | 4 | from utils_bot.datetime import TZ, datetime 5 | from utils_bot.logging import logger 6 | 7 | 8 | # gets current time 9 | def getTime() -> str: 10 | return datetime.now(TZ).strftime('%m-%d %H:%M:%S') 11 | 12 | # a page that always works for the program purpose 13 | url: str = "https://www.baidu.com/s?wd=sousuoredian" 14 | headers: dict = { 15 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36', 16 | # 'Cookie': 'sometimes you need this' 17 | } 18 | 19 | async def get_baidu_trend(opt: str) -> str: 20 | try: 21 | async with aiohttp.ClientSession() as session: 22 | res = await session.get(url, headers=headers, timeout=3) 23 | res.raise_for_status() 24 | soup = bs4.BeautifulSoup(await res.read(), features='lxml') 25 | 26 | titles = soup.select('a.opr-toplist1-subtitle') #| 27 | #pops = soup.select('.opr-toplist1-right') #| place for updates 28 | length = len(titles) 29 | 30 | if opt != 'all': 31 | length = 6 32 | 33 | res = ' NEWS\n' 34 | for j in range(length): 35 | res += titles[j].getText().strip() + '\n' 36 | res += 'fetched %s with love. Source: www.baidu.com' % getTime() 37 | return res 38 | except Exception as e: 39 | logger.exception(e) 40 | return 'an error occurred' 41 | -------------------------------------------------------------------------------- /plugins/custom_reply/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import re 4 | from os import path 5 | 6 | import asyncio 7 | 8 | from aiocqhttp import Event 9 | from nonebot import CommandSession, get_bot, on_command 10 | from nonebot.permission import * 11 | 12 | from utils_bot.command_ops import force_private 13 | from utils_bot.logging import logger 14 | from utils_bot.typing import Union 15 | 16 | 17 | FAILED_MSG = '查看关键字对:view [群号] [模式]\n'\ 18 | '添加单条消息:add [群号] [模式]\n'\ 19 | '删除单条消息:del [群号] [模式]\n'\ 20 | '删除指定群:delall [群号]\n'\ 21 | '模式:完全匹配-1,包含匹配-2,正则匹配-3' 22 | 23 | __plugin_name__ = '自定义群聊回复 (private)' 24 | __plugin_usage__ = r'''feature: 自定义回复 25 | 26 | 从 json 文件读取自定义回复并且响应群聊。 27 | 可以私聊机器人添加或删除机器人,其改变即时生效 28 | ''' + FAILED_MSG 29 | 30 | # define reply data file path 31 | DATA_PATH = path.join(path.dirname(__file__), 'group_data.json') 32 | 33 | DEFAULT_GROUP_DICT: dict = { 34 | "full_match": {}, 35 | "inclusive_match": {}, 36 | "regex_match" : {} 37 | } 38 | 39 | def load_data() -> dict: 40 | if not path.exists(DATA_PATH): 41 | with open(DATA_PATH, 'w') as datafile: 42 | json.dump({ 43 | "global": DEFAULT_GROUP_DICT 44 | }, datafile, indent=4) 45 | with open(DATA_PATH) as datafile: 46 | return json.load(datafile) 47 | 48 | # load reply data 49 | REPLIES: dict = load_data() 50 | 51 | # acquires global event monitor 52 | bot = get_bot() 53 | 54 | # auto reply in group chats 55 | 56 | def process_var(ctx: Event, myText: str) -> str: 57 | 'process whether the reply keyword contains variables.' 58 | if not re.search(r'{SENDER_.+]', myText): 59 | return myText 60 | # define pointer constants 61 | SENDER_ID: str = str(ctx['sender']['user_id']) 62 | SENDER_NICK: str = ctx['sender']['nickname'] 63 | SENDER_CARD: str = ctx['sender']['card'] 64 | SENDER_ROLE: str = ctx['sender']['role'] 65 | SENDER_TITLE: str = ctx['sender']['title'] 66 | # when you chose python, you gave up efficiency... 67 | return myText.replace('{SENDER_ID]', SENDER_ID).\ 68 | replace('{SENDER_NICK]', SENDER_NICK).\ 69 | replace('{SENDER_CARD]', SENDER_CARD).\ 70 | replace('{SENDER_ROLE]', SENDER_ROLE).\ 71 | replace('{SENDER_TITLE]', SENDER_TITLE) 72 | 73 | class _Get_Out(Exception): 74 | pass 75 | 76 | @bot.on_message('group') 77 | async def handle_keyword_reply(ctx: Event): 78 | 79 | currentGroupId: int = ctx['group_id'] 80 | textReceived: str = ctx['raw_message'] 81 | toSend: Union[str, None] = None 82 | 83 | # config specific to groups is prioritized 84 | for groupId in (str(currentGroupId), 'global'): 85 | try: 86 | # handles full_match 87 | mayReply = REPLIES[groupId]['full_match'].get(textReceived, None) 88 | if mayReply is not None: 89 | toSend = process_var(ctx, random.choice(mayReply)) 90 | raise _Get_Out 91 | # handles inclusive_match 92 | for keyword, reply in REPLIES[groupId]['inclusive_match'].items(): 93 | if keyword in textReceived: 94 | toSend = process_var(ctx, random.choice(reply)) 95 | raise _Get_Out 96 | # handles regex_match 97 | for keyword, reply in REPLIES[groupId]['regex_match'].items(): 98 | if re.search(keyword, textReceived): 99 | toSend = process_var(ctx, random.choice(reply)) 100 | raise _Get_Out 101 | except KeyError: 102 | # REPLIES[str(groupId)] may not exist, go to global 103 | pass 104 | except _Get_Out: 105 | break 106 | if toSend is not None: 107 | # waits few secs before sending message 108 | await asyncio.sleep(random.randint(1,5)) 109 | await bot.send_group_msg(group_id=currentGroupId, message=toSend) 110 | 111 | 112 | # superuser can modify replies 113 | 114 | class keyword_ops: 115 | 'operations on keyword dict' 116 | def __init__(self, repliesDict, 117 | order='', groupId='', mode='', keyword='', reply=''): 118 | """ 119 | :param order: 'add', 'del', 'delall' or 'view' 120 | :param groupId: digits or 'global' 121 | :param mode: '1', '2' or '3' 122 | :param keyword: keyword 123 | :param reply: reply 124 | """ 125 | if order not in ('add', 'del', 'delall', 'view'): 126 | raise Exception("指令错误\n" + FAILED_MSG) 127 | if not groupId.isdecimal() and groupId != 'global': 128 | raise Exception("群号错误\n" + FAILED_MSG) 129 | if order != 'delall': 130 | if mode == '1': 131 | self.modeStr: str = 'full_match' 132 | elif mode == '2': 133 | self.modeStr: str = 'inclusive_match' 134 | elif mode == '3': 135 | self.modeStr: str = 'regex_match' 136 | else: 137 | raise Exception("模式错误\n" + FAILED_MSG) 138 | 139 | from copy import deepcopy 140 | self.repliesDict: dict = deepcopy(repliesDict) 141 | self.order: str = order 142 | self.groupId: str = groupId 143 | self.mode: str = mode 144 | self.keyword: str = keyword 145 | self.reply: str = reply 146 | 147 | def add(self): 148 | self.repliesDict.setdefault(self.groupId, DEFAULT_GROUP_DICT) 149 | modifyPlace = self.repliesDict[self.groupId][self.modeStr] 150 | modifyPlace.setdefault(self.keyword, []) 151 | modifyPlace[self.keyword].append(self.reply) 152 | 153 | def dele(self): 154 | del self.repliesDict[self.groupId][self.modeStr][self.keyword] 155 | 156 | def delall(self): 157 | if self.groupId == 'global': 158 | self.repliesDict['global'] = DEFAULT_GROUP_DICT 159 | else: 160 | del self.repliesDict[self.groupId] 161 | 162 | @staticmethod 163 | def backup(): 164 | from shutil import copyfile 165 | copyfile(DATA_PATH, path.join(path.dirname(__file__), 'group_data_bak.json')) 166 | 167 | def rewrite(self): 168 | with open(DATA_PATH, 'w') as datafile: 169 | json.dump(self.repliesDict, datafile, indent=4, ensure_ascii=False) 170 | 171 | def send(self): 172 | return self.repliesDict 173 | 174 | def view_keywords(self) -> str: 175 | 'NOTHROW. returns the keyword pairs for group --> mode' 176 | try: 177 | pos: dict = self.repliesDict[self.groupId][self.modeStr] 178 | res: str = f'{self.groupId}, {self.modeStr}\n' 179 | for k, v in pos.items(): 180 | if len(v) > 10: 181 | v = v[:10] + ['...'] 182 | res += '"{}": "{}"\n'.format(k, '"; "'.join(v)) 183 | if pos == {}: 184 | res += '(空)' 185 | res = res.rstrip('\n') 186 | except KeyError: 187 | res = '群号错误?' 188 | return res 189 | 190 | def modify_keywords(self) -> dict: 191 | 'modifies the keyword json file, returns the new keyword dict' 192 | try: 193 | if self.order == 'add': 194 | self.add() 195 | elif self.order == 'del': 196 | self.dele() 197 | elif self.order == 'delall': 198 | self.delall() 199 | except KeyError: 200 | raise Exception('当前关键字表不可用或无此关键字') 201 | 202 | try: 203 | self.backup() 204 | except Exception as exc: 205 | raise Exception('出现错误。 当前设置没有改动' + str(exc)) 206 | self.rewrite() 207 | 208 | return self.send() 209 | 210 | 211 | # keyword interface 212 | @on_command('群关键字', permission=SUPERUSER) 213 | @force_private 214 | async def keyword_mod(session: CommandSession): 215 | 216 | order, groupId, mode = '', '', '' 217 | iniParam: list = session.get('iniParam') 218 | try: 219 | order = iniParam[0] 220 | groupId = iniParam[1] 221 | mode = iniParam[2] 222 | except Exception: 223 | pass 224 | logger.info(f'keyword modification called: {order}; {groupId}; {mode}') 225 | ################################################################## 226 | 227 | global REPLIES 228 | 229 | # get keyword mod object, reuse if this command is recalled and continued 230 | if not session.state.get('keymod_obj'): 231 | try: 232 | keymod = keyword_ops(REPLIES, order, groupId, mode) 233 | session.state['keymod_obj'] = keymod 234 | except Exception as exc: 235 | session.finish(str(exc)) 236 | else: 237 | keymod = session.state['keymod_obj'] 238 | # VIEW order 239 | if order == 'view': 240 | session.finish(keymod.view_keywords()) 241 | # MODIFY order 242 | if order in ['add', 'del']: 243 | keymod.keyword = session.get('keyword', prompt='在这里输入关键字') 244 | if order == 'add': 245 | keymod.reply = session.get('reply', prompt='在这里输入回复') 246 | try: 247 | REPLIES = keymod.modify_keywords() 248 | session.finish('success!') 249 | except Exception as exc: 250 | session.finish(str(exc)) 251 | 252 | 253 | @keyword_mod.args_parser 254 | @force_private 255 | async def keyword_mod_arg(session: CommandSession): 256 | argStripped = session.current_arg_text.strip() 257 | 258 | if session.is_first_run: 259 | if argStripped: 260 | session.state['iniParam'] = argStripped.split(' ') 261 | # if no arg given at first 262 | else: 263 | session.finish(FAILED_MSG) 264 | elif session.current_key == 'keyword': 265 | session.state['keyword'] = argStripped 266 | elif session.current_key == 'reply': 267 | session.state['reply'] = argStripped 268 | -------------------------------------------------------------------------------- /plugins/custom_reply/group_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "modes": { 3 | "full match": 1, 4 | "inclusive match": 2, 5 | "regex match": 3 6 | }, 7 | "global": { 8 | "full_match": { 9 | "千堂食咩": [ 10 | "梅菜扣肉", "菠萝咕噜肉", "蒜蓉粉丝蒸大虾", "清蒸桂鱼", "腊肠炒荷兰豆", 11 | "菠萝咕噜豆腐", "叉烧肉", "卷肠粉", "冬瓜丸子汤", "白灼虾", "香菇虾仁糯米烧麦", 12 | "菠萝咕佬肉", "酸菜烩牛杂", "蜜汁叉烧包", "白切鸡", "砂锅盐焗鸡", "牛肉河粉", 13 | "葱香豉油鸭腿", "鲜虾肠粉", "香辣炒红薯粉", "蒜蓉粉丝蒸虾", "叉烧肉", "猪脚粉", 14 | "螺蛳粉", "南宁粉饺", "不饿", "人肉叉烧包" 15 | ], 16 | "千堂吃什么" : [ 17 | "麻婆豆腐", "辣子鸡丁", "东坡肘子", "豆瓣鲫鱼", "口袋豆腐", "酸菜鱼", "夫妻肺片", 18 | "蚂蚁上树", "叫花鸡", "茄汁肉卷", "鱼香肉丝", "干煸冬笋", "魔芋烧鸭", "锅贴鱼片", 19 | "麻辣肉丁", "鱼香茄饼", "冬菜肉末", "粉蒸鸡", "四川火锅", "重庆火锅", "牛油火锅", 20 | "不饿", "宫保鸡丁", "水煮鱼", "刀削面", "炸酱面", "油泼面", "番茄炒面", "麻酱凉面", 21 | "重庆砂锅米线", "云南过桥米线", "牛肉拉面", "兰州牛肉面", "西红柿鸡蛋面", 22 | "白菜猪肉炖粉条" 23 | ], 24 | "大小姐吃什么" : [ 25 | "Coquilles Saint-Jacques", "Baked Camembert", "Moules Marinières", "Buckwheat Crêpes", 26 | "Blanquette de Veau", "Soupe à L'oignon", "Sole Meunière", "Hachis Parmentier", 27 | "Boudin Noir Aux Pommes", "Cheese Soufflé", "Steak Tartare", "Pot-au-feu", "Piperade", 28 | "Magret de Canard", "Garbure", "Cassoulet", "Pan-seared Foie Gras", "Confit de Canard", 29 | "Poulet Basquaise", "Lamprey à la Bordelaise", "Quenelles of Pike with Lobster Sauce", 30 | "Soupe de Poisson à la Rouille", "Gigot D'Agneau Pleureur", "Bouillabaisse" 31 | ], 32 | "早安": [ 33 | "早安![\"┗|`O′|┛ 起床了嗷~~" 34 | ], 35 | "晚安": [ 36 | "晚安 ( ̄o ̄) . z Z Bye Bye" 37 | ], 38 | "$test_variable": [ 39 | "你的QQ: {SENDER_ID]\n你的昵称:{SENDER_NICK]" 40 | ] 41 | }, 42 | "inclusive_match": { 43 | }, 44 | "regex_match": { 45 | "^[A-Z]{3,4}[-\\._]?[0-9]{3,5}$": [ 46 | "?" 47 | ] 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /plugins/evaler/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import CommandSession, on_command 2 | from nonebot.permission import * 3 | 4 | from utils_bot.command_ops import global_cooldown 5 | from utils_bot.logging import logger 6 | 7 | from .data_source_glot_run import SUPPORTED_LANGS, code_run_glot 8 | 9 | __plugin_name__ = 'coderunner' 10 | __plugin_usage__ = f'''feature: 执行代码 11 | 12 | run [lang](这里空行)[statements] 执行语句 13 | 支持语言:{', '.join(SUPPORTED_LANGS.keys())} 14 | 15 | 示例: 16 | run python 17 | print(1+1) 18 | 19 | attribution: https://glot.io 20 | ''' 21 | 22 | 23 | @on_command('coderunner', aliases=('运行', '编译', 'run', 'exe'), permission=SUPERUSER | GROUP_MEMBER, only_to_me=False) 24 | @global_cooldown(15) 25 | async def coderunner(session: CommandSession): 26 | argsList: tuple = session.get('args') 27 | res = await code_run_glot(argsList[0], argsList[1]) 28 | await session.send(res) 29 | logger.info(f'coderunner called: {res[:20]}...') 30 | 31 | @coderunner.args_parser 32 | async def _(session: CommandSession): 33 | argsStripped: str = session.current_arg_text.strip() 34 | 35 | if session.is_first_run: 36 | if argsStripped: 37 | argsList: list = argsStripped.split('\n', 1) 38 | if len(argsList) < 2: 39 | session.finish(__plugin_usage__) 40 | 41 | lang, code = argsList[0].strip().lower(), argsList[1] 42 | # manual correction 43 | if lang == 'py': lang = 'python' 44 | elif lang == 'c++': lang = 'cpp' 45 | elif lang == 'c#': lang = 'csharp' 46 | elif lang == 'js': lang = 'javascript' 47 | elif lang == 'f#': lang = 'fsharp' 48 | 49 | if not lang in SUPPORTED_LANGS.keys(): 50 | session.finish('当前语言不支持~') 51 | 52 | session.state['args'] = (lang, code) 53 | else: 54 | session.finish(__plugin_usage__) 55 | -------------------------------------------------------------------------------- /plugins/evaler/data_source_glot_run.py: -------------------------------------------------------------------------------- 1 | #from aiohttp import request 2 | import aiohttp 3 | from nonebot import get_bot 4 | 5 | from utils_bot.logging import logger 6 | from utils_bot.typing import Union 7 | 8 | # code running api source: https://github.com/prasmussen/glot-run/ 9 | # api url and doc: https://glot.io 10 | # get your token there at https://glot.io/account/token 11 | ## 12 | # program attribution: https://github.com/cczu-osa/aki/ 13 | 14 | RUN_API_URL_FORMAT: str = 'https://glot.io/run/{}?version=latest' 15 | 16 | TOKEN: str = get_bot().config.GLOT_RUN_TOKEN 17 | 18 | SUPPORTED_LANGS: dict = { 19 | 'assembly': {'ext': 'asm'}, 20 | 'bash': {'ext': 'sh'}, 21 | 'c': {'ext': 'c'}, 22 | 'clojure': {'ext': 'clj'}, 23 | 'coffeescript': {'ext': 'coffe'}, 24 | 'cpp': {'ext': 'cpp'}, 25 | 'csharp': {'ext': 'cs'}, 26 | 'erlang': {'ext': 'erl'}, 27 | 'fsharp': {'ext': 'fs'}, 28 | 'go': {'ext': 'go'}, 29 | 'groovy': {'ext': 'groovy'}, 30 | 'haskell': {'ext': 'hs'}, 31 | 'java': {'ext': 'java', 'name': 'Main'}, 32 | 'javascript': {'ext': 'js'}, 33 | 'julia': {'ext': 'jl'}, 34 | 'kotlin': {'ext': 'kt'}, 35 | 'lua': {'ext': 'lua'}, 36 | 'perl': {'ext': 'pl'}, 37 | 'php': {'ext': 'php'}, 38 | 'python': {'ext': 'py'}, 39 | 'ruby': {'ext': 'rb'}, 40 | 'rust': {'ext': 'rs'}, 41 | 'scala': {'ext': 'scala'}, 42 | 'swift': {'ext': 'swift'}, 43 | 'typescript': {'ext': 'ts'}, 44 | } 45 | 46 | headers = {'Authorization': f'Token {TOKEN}'} 47 | 48 | async def fetch(lang: str, code: str) -> Union[dict, None]: 49 | async with aiohttp.ClientSession() as session: 50 | try: 51 | async with session.post(RUN_API_URL_FORMAT.format(lang), 52 | headers=headers, raise_for_status=True, timeout=10, 53 | json={ 54 | 'files': [{ 55 | 'name': (SUPPORTED_LANGS[lang].get('name', 'main') + 56 | f'.{SUPPORTED_LANGS[lang]["ext"]}'), 57 | 'content': code 58 | }], 59 | 'stdin': '', 60 | 'command': '' 61 | }) as req: 62 | req.raise_for_status() 63 | payload: dict = await req.json() 64 | except Exception as e: 65 | logger.exception(e) 66 | return None 67 | 68 | return payload 69 | 70 | def process_result(payload: Union[dict, None]) -> str: 71 | if payload is None: 72 | return '不可用' 73 | res: str = '' 74 | try: 75 | for k in ('stdout', 'stderr', 'error'): 76 | val = payload.get(k) 77 | 78 | lines: list = val.splitlines() 79 | lines, linesRemain = lines[:15], lines[15:] 80 | out, outRemain = val[:80 * 10], val[80 * 10:] 81 | 82 | isCut = '(输出过长,已截断)\n' if linesRemain or outRemain else '' 83 | Add2End = '' if val or k == 'error' else '\n' 84 | res += f'{k}: {isCut}{out}{Add2End}' 85 | 86 | except Exception: 87 | pass 88 | return res 89 | 90 | async def code_run_glot(lang: str, code: str) -> str: 91 | return process_result(await fetch(lang, code)) 92 | -------------------------------------------------------------------------------- /plugins/exec.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import pprint 3 | from typing import Awaitable 4 | 5 | from nonebot import CommandSession, on_command 6 | from nonebot.message import unescape 7 | from nonebot.permission import * 8 | 9 | from utils_bot.command_ops import force_private 10 | 11 | __plugin_name__ = '远程执行 python (private)' 12 | __plugin_usage__ = f'''feature: 执行代码 13 | (根目录:bot.py文件所在目录) 14 | remote [statements] 15 | ''' 16 | # source: https://github.com/cczu-osa/aki/ 17 | 18 | 19 | @on_command('remote', permission=SUPERUSER) 20 | @force_private 21 | async def _(session: CommandSession): 22 | code = unescape(session.current_arg or '') 23 | 24 | try: 25 | localArgs = {} 26 | exec(code, None, localArgs) 27 | await session.send(f'Locals:\n{pprint.pformat(localArgs, indent=2)}') 28 | 29 | if callable(localArgs.get('run')): 30 | res = localArgs['run'](session.bot, session.event) 31 | if isinstance(res, Awaitable): 32 | res = await asyncio.wait_for(res, 6) 33 | await session.send(f'返回:\n{pprint.pformat(res, indent=2)}') 34 | except Exception as exc: 35 | await session.send(f'执行失败\n异常:\n{pprint.pformat(exc, indent=2)}') 36 | -------------------------------------------------------------------------------- /plugins/gamble/__init__.py: -------------------------------------------------------------------------------- 1 | from jieba import posseg 2 | from nonebot import (CommandSession, IntentCommand, NLPSession, on_command, 3 | on_natural_language) 4 | from nonebot.permission import * 5 | 6 | from utils_bot.typing import Callable, Generator, Union 7 | 8 | from .diceroll import * 9 | 10 | __plugin_name__ = '试试人品 *NL' 11 | __plugin_usage__ = f'''feature: 生成随机数 12 | 可用命令: 13 | 扔骰子, 扔骰子 []次 14 | 扔硬币, 扔硬币 []次 15 | ''' 16 | 17 | # help session 18 | @on_command('试试人品', permission=GROUP_MEMBER | SUPERUSER) 19 | async def try_luck(session: CommandSession): 20 | await session.send(__plugin_usage__) 21 | 22 | class random_ops: 23 | @staticmethod 24 | async def evaluate(session: CommandSession, f: Callable): 25 | times: int = session.get('times') 26 | await session.send(f(**( {'times': times} if times else {} ))) 27 | 28 | @staticmethod 29 | def nl_proc(session: NLPSession) -> Generator[Union[str, None], None, None]: 30 | argsStripped: str = session.msg_text.strip() 31 | words = posseg.lcut(argsStripped) 32 | 33 | for word in words: 34 | if word.flag == 'm': 35 | yield word.word 36 | yield None 37 | 38 | @on_command('扔骰子', aliases=('扔色子', '扔个骰子', '扔个色子'), permission=GROUP_MEMBER | SUPERUSER) 39 | async def roll_dice_host(session: CommandSession): 40 | await random_ops.evaluate(session, roll_dice_many) 41 | 42 | 43 | @on_command('扔硬币', aliases=('扔个硬币', '扔钢镚', '扔个钢蹦'), permission=GROUP_MEMBER | SUPERUSER) 44 | async def flip_coin_host(session: CommandSession): 45 | await random_ops.evaluate(session, flip_coin_many) 46 | 47 | 48 | 49 | @roll_dice_host.args_parser 50 | @flip_coin_host.args_parser 51 | async def _(session: CommandSession): 52 | argsStripped: str = session.current_arg_text.strip(' \n次') 53 | session.state['times'] = argsStripped if argsStripped else None 54 | 55 | 56 | # EXP 57 | @on_natural_language(keywords={'骰子', '色子'}, permission=SUPERUSER | GROUP_MEMBER) 58 | async def _(session: NLPSession): 59 | times = next(random_ops.nl_proc(session)) 60 | return IntentCommand(64.0, '扔骰子', current_arg=times or '') 61 | 62 | @on_natural_language(keywords={'硬币', '钢镚'}, permission=SUPERUSER | GROUP_MEMBER) 63 | async def _(session: NLPSession): 64 | times = next(random_ops.nl_proc(session)) 65 | return IntentCommand(64.0, '扔硬币', current_arg=times or '') 66 | -------------------------------------------------------------------------------- /plugins/gamble/diceroll.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from utils_bot.typing import Any 4 | 5 | __all__ = ['roll_dice_many', 6 | 'flip_coin_many'] 7 | 8 | DICE_RESULTS = ( 9 | '0', 10 | ''' 11 | . 12 | /| 13 | | 14 | | 15 | _|_ 16 | ''', 17 | ''' 18 | .:::.: 19 | .: .: 20 | .:: 21 | .:: 22 | .:: 23 | .::::::: 24 | ''', 25 | r''' 26 | ____ 27 | |___ \ 28 | __) | 29 | |__ < 30 | ___) | 31 | |____/ 32 | ''', 33 | ''' 34 | _ _ 35 | | || | 36 | | || |_ 37 | |__ _| 38 | | | 39 | |_| 40 | ''', 41 | r''' 42 | _____ 43 | | ____| 44 | | |__ 45 | |___ \ 46 | ___) | 47 | |____/ 48 | ''', 49 | ''' 50 | 666666 51 | 6:::::: 52 | 6::::::6 53 | 6::::::6 54 | 6::::::6 55 | 6::::::6 56 | 6::::::6 57 | 6::::::::6666 58 | 6::::::::::::: 59 | 6::::::66666:: 60 | 6:::::6 6: 61 | 6:::::6 6: 62 | 6::::::66666:: 63 | 66::::::::::: 64 | 66::::::::: 65 | 666666666 66 | ''', 67 | ) 68 | 69 | def roll_dice(pixel:bool=False) -> str: 70 | '''displays '1'-'6' randomly. if pixel is true then displays image 71 | ''' 72 | res: int = random.randint(1, 6) 73 | if pixel: 74 | return DICE_RESULTS[res].strip() 75 | else: 76 | return str(res) 77 | 78 | def roll_dice_many(times:Any=1) -> str: 79 | 'have to assure [times] is in the form of an int...' 80 | res: str = '' 81 | try: 82 | times = int(times) 83 | if times == 1: 84 | return roll_dice(pixel=True) 85 | 86 | if times > 300000: 87 | raise TimeoutError 88 | resDict = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0} 89 | for _ in range(times): 90 | r = roll_dice() 91 | resDict[r] += 1 92 | for j in ('1', '2', '3', '4', '5', '6'): 93 | res += f'{j}: {resDict[j]}次\n' 94 | return res.strip() 95 | except TimeoutError: 96 | return '次数太多扔不过来啦~' 97 | except ValueError: 98 | return '?' 99 | 100 | 101 | def flip_coin() -> str: 102 | res: int = random.randint(0, 1) 103 | return ('反', '正')[res] 104 | 105 | def flip_coin_many(times:Any=1) -> str: 106 | 'have to assure [times] is in the form of an int...' 107 | try: 108 | times = int(times) 109 | if times == 1: 110 | return flip_coin() 111 | 112 | if times > 300000: 113 | raise TimeoutError 114 | resList = [0, 0] 115 | for _ in range(times): 116 | r: int = random.randint(0, 1) 117 | resList[r] += 1 118 | return f'正:{resList[1]} 反:{resList[0]}' 119 | except TimeoutError: 120 | return '次数太多扔不过来啦~' 121 | except ValueError: 122 | return '?' 123 | -------------------------------------------------------------------------------- /plugins/notifyme/__init__.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from apscheduler.job import Job 4 | from nonebot import (CommandSession, IntentCommand, MessageSegment, NLPSession, 5 | get_bot, on_command, on_natural_language, scheduler) 6 | from nonebot.permission import * 7 | 8 | from utils_bot.datetime import TZ, datetime, timedelta 9 | from utils_bot.logging import logger 10 | 11 | __plugin_name__ = '定时提醒 *NL' 12 | __plugin_usage__ = f'''feature: 在XX小时之后提醒用户 13 | 用法: 14 | 定时提醒 [H] [M] [信息] 我将在 H 时 M 分 后在本群发送信息提醒用户 15 | 取消定时提醒 撤销用户在本群设定的定时提醒 16 | ''' 17 | 18 | MAX_HOURS = 25 19 | 20 | 21 | def get_job_id(session: CommandSession) -> str: 22 | user = session.event['user_id'] 23 | group = session.event.get('group_id', 0) 24 | return f'notifyme_qq{user}_grp{group}' 25 | 26 | 27 | def fmt_job_scheduled_time(job: Job) -> str: 28 | return job.trigger.run_date.strftime('%m月%d日 %H:%M') 29 | 30 | 31 | async def send_notify(user: int, msg: str, group:int=0): 32 | 'group=0 means to send the message to private chat' 33 | bot = get_bot() 34 | if group != 0: 35 | sendmsg = MessageSegment.at(user) + ' ' + msg 36 | await bot.send_group_msg(group_id=group, message=sendmsg) 37 | else: 38 | await bot.send_private_msg(user_id=user, message=msg) 39 | logger.info(f'user {user} finished notifyme job in group {group}') 40 | 41 | 42 | @on_command('定时提醒', aliases=('闹钟'), permission=GROUP_MEMBER | SUPERUSER) 43 | async def notifyme_set(session: CommandSession): 44 | job_id = get_job_id(session) 45 | existing_job = scheduler.get_job(job_id) 46 | 47 | if existing_job: 48 | time = fmt_job_scheduled_time(existing_job) 49 | msg = existing_job.args[1] 50 | session.finish(f'您已有设定的定时提醒~\n{time} {msg} \n告诉我"取消定时提醒"来取消它') 51 | 52 | user, group = session.event['user_id'], session.event.get('group_id', 0) 53 | tdelta = session.get('tdelta') 54 | msg = session.get('msg') 55 | 56 | new_job = scheduler.add_job(send_notify, 'date', 57 | run_date=datetime.now(TZ) + tdelta, 58 | id=job_id, 59 | args=(user, msg, group) 60 | ) 61 | 62 | time = fmt_job_scheduled_time(new_job) 63 | msg = new_job.args[1] 64 | await session.send(f'已设定:\n{time} {msg}') 65 | logger.info(f'user {user} finished notifyme job in group {group}') 66 | 67 | @notifyme_set.args_parser 68 | async def _(session: CommandSession): 69 | argsStripped = session.current_arg_text.strip() or '' 70 | args = argsStripped.split(' ', 2) 71 | 72 | if len(args) == 3: 73 | try: 74 | tdelta = timedelta(hours=int(args[0]), minutes=int(args[1])) 75 | if tdelta > timedelta(hours=MAX_HOURS) or tdelta < timedelta(): 76 | session.finish(f'最多只能设定到{MAX_HOURS}小时~') 77 | session.state['tdelta'] = tdelta 78 | session.state['msg'] = args[2] 79 | except (ValueError, IndexError): 80 | session.finish(__plugin_usage__) 81 | else: 82 | session.finish(__plugin_usage__) 83 | 84 | @on_natural_language(keywords={'提醒我'}, permission=SUPERUSER | GROUP_MEMBER) 85 | async def _(session: NLPSession): 86 | sentence = session.msg_text.replace('提醒我', '', 1).strip() 87 | 88 | def r(regex): 89 | search = re.findall(regex, sentence) 90 | return search[0] if search != [] else search 91 | 92 | # re.findall(regex,'5小时3分钟后(提醒我)xxxxxyyy') 93 | # -> [('5小时', '5', '小时', '3分钟', '3', '分钟', 'xxxxxyyy')] 94 | search = r(r'((\d+) ?(小?时|h|H))? ?((\d+) ?(分钟?|min))?后?(.+)$') 95 | if len(search) == 7 and (search[1] or search[4]): 96 | return IntentCommand(64.0, '定时提醒', current_arg=f'{search[1] or 0} {search[4] or 0} {search[6]}') 97 | # re.findall(regex, '13:42后xxxx') 98 | # -> [('13', '42', 'xxxx')] 99 | search = r(r'(\d{1,2}):(\d{1,2}) ?后(.+)$') 100 | if len(search) == 3: 101 | return IntentCommand(64.0, '定时提醒', current_arg=f'{search[0]} {search[1]} {search[2]}') 102 | # re.findall(regex, '13:42xxxx') 103 | # -> [('13', ':', '42', 'xxxx')] 104 | # TODO: this part is improvable 105 | search = r(r'(\d{2})(:|点)(\d{2}) ?(.+)$') 106 | if len(search) == 4: 107 | now = datetime.now(TZ) 108 | dt = (now.replace(hour=int(search[0]), minute=int(search[2]), 109 | second=0, microsecond=0) - now).total_seconds() 110 | return IntentCommand(64.0, '定时提醒', current_arg=f'0 {int(dt/60)+1} {search[3]}') 111 | 112 | return IntentCommand(0, '定时提醒') 113 | 114 | 115 | @on_command('取消定时提醒', aliases=('撤销定时提醒',), permission=GROUP_MEMBER | SUPERUSER) 116 | async def notifyme_revoke(session: CommandSession): 117 | job = scheduler.get_job(get_job_id(session)) 118 | if job: 119 | time = fmt_job_scheduled_time(job) 120 | job.remove() 121 | await session.send(f'已取消预定于 {time} 的定时提醒') 122 | else: 123 | await session.send('您没有设定定时提醒~') 124 | -------------------------------------------------------------------------------- /plugins/personality/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import CommandSession as CmdS 2 | from nonebot.permission import * 3 | 4 | from utils_bot.command_ops import (on_grp_command, on_grp_command_ask, 5 | on_grp_command_do) 6 | from utils_bot.string_ops import half_none, prob_pick 7 | 8 | __plugin_usage__ = '一些个性化回复' 9 | 10 | @on_grp_command_ask('你是谁', '是谁', '谁') 11 | async def _(s: CmdS): await s.send('sendo erika!') 12 | 13 | @on_grp_command_ask('性别', '你的性别') 14 | async def _(s: CmdS): await s.send(f'女{half_none("!")}') 15 | 16 | @on_grp_command_ask('年龄', '你的年龄') 17 | async def _(s: CmdS): await s.send(f'16{half_none("!")}') 18 | 19 | @on_grp_command_ask('主人是谁', '你的主人是谁', '谁是你的主人') 20 | async def _(s: CmdS): await s.send('没有') 21 | 22 | @on_grp_command_ask('你爱我吗', '你爱不爱我', '爱我吗', '爱不爱我') 23 | async def _(s: CmdS): await s.send('想要获得本小姐的青睐,可不是容易的事情噢~') 24 | 25 | @on_grp_command_do('结婚', '嫁给我', '我娶你') 26 | async def _(s: CmdS): await s.send('绝对不行') 27 | 28 | @on_grp_command('我爱你') 29 | async def _(s: CmdS): await s.send('绝对不行') 30 | 31 | # | | | | ? 32 | # v v v v 33 | @on_grp_command_do('娇喘', '淫叫', '叫床') 34 | async def _(s: CmdS): 35 | reply = ('你怎么了?', '哈?', '哈??', '嗯……', 36 | '……怎么突然说这种话', 37 | '…哈……哈…唔嗯…………唔哈哈~……啊啊啊', 38 | '嗯,咯……!啊啊,呀,啊嗯,呷,呼啊,哈……,啊\n——我们,一起!') 39 | prob = (.1, .35, .6, .75, .9, .96, 1) 40 | await s.send(prob_pick(reply, prob)) 41 | -------------------------------------------------------------------------------- /plugins/remotely/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import CommandSession, get_bot, load_plugins, on_command 2 | from nonebot.exceptions import CQHttpError 3 | from nonebot.message import unescape 4 | from nonebot.permission import * 5 | 6 | from utils_bot.typing import depreciated 7 | 8 | __plugin_name__ = '遥控机器人 (private)' 9 | __plugin_usage__ = r'''feature: 遥控 10 | 发送消息: 11 | 发送到群 [群号] [内容] 12 | 发送到QQ [QQ号] [内容] 13 | 14 | 发送CQ码: 15 | 发送到群CQ [群号] [内容] 16 | 发送到QQCQ [QQ号] [内容] 17 | 18 | 改群名片: 19 | 改群名片 [群号] [内容 | ''] (内容为空代表删除) 20 | 21 | 查看已加入的群: 22 | 所在的群 23 | ''' 24 | 25 | async def send_to_x(session: CommandSession, msg_type: str, unescape_=False): 26 | bot = get_bot() 27 | targetId, toSend = session.get('id'), session.get('msg') 28 | if unescape_: 29 | toSend = unescape(toSend) 30 | 31 | try: 32 | if msg_type == 'group': 33 | await bot.send_group_msg(group_id=targetId, message=toSend) 34 | elif msg_type == 'private': 35 | await bot.send_private_msg(user_id=targetId, message=toSend) 36 | await session.send('success!') 37 | except CQHttpError as exc: 38 | await session.send(str(exc)) 39 | 40 | @on_command('发送到群', permission=SUPERUSER, privileged=True) 41 | async def send_to_group(session: CommandSession): 42 | await send_to_x(session, 'group') 43 | 44 | @on_command('发送到QQ', permission=SUPERUSER, privileged=True) 45 | async def send_to_private(session: CommandSession): 46 | await send_to_x(session, 'private') 47 | 48 | @on_command('发送到群CQ', permission=SUPERUSER, privileged=True) 49 | async def send_to_group_CQ(session: CommandSession): 50 | await send_to_x(session, 'group', True) 51 | 52 | @on_command('发送到QQCQ', permission=SUPERUSER, privileged=True) 53 | async def send_to_private_CQ(session: CommandSession): 54 | await send_to_x(session, 'private', True) 55 | 56 | @send_to_group_CQ.args_parser 57 | @send_to_private_CQ.args_parser 58 | @send_to_group.args_parser 59 | @send_to_private.args_parser 60 | async def _(session: CommandSession): 61 | argStripped = session.current_arg.strip() or '' 62 | args = argStripped.split(' ', 1) 63 | if len(args) == 2: 64 | session.state['id'] = args[0] 65 | session.state['msg'] = args[1] 66 | else: 67 | session.finish('用法:\n发送到群/QQ [群号/QQ] [内容]') 68 | 69 | 70 | @on_command('改群名片', permission=SUPERUSER, privileged=True) 71 | async def change_my_group_card(session: CommandSession): 72 | bot = get_bot() 73 | targetId, card = session.get('id'), session.get('card') 74 | try: 75 | await bot.set_group_card(group_id=targetId, user_id=bot.config.SELF_QQ, card=card) 76 | await session.send('success!') 77 | except CQHttpError as exc: 78 | await session.send(str(exc)) 79 | 80 | @change_my_group_card.args_parser 81 | async def _(session: CommandSession): 82 | argStripped = session.current_arg.strip() or '' 83 | args = argStripped.split(' ', 1) if argStripped else [] 84 | if len(args) >= 1: 85 | session.state['id'] = args[0] 86 | session.state['card'] = args[1] if len(args) >= 2 else '' 87 | else: 88 | session.finish('用法:\n改群名片 [群号] [内容 | ""]') 89 | 90 | 91 | @on_command('所在的群', permission=SUPERUSER, privileged=True) 92 | async def groups_in(session: CommandSession): 93 | groups = await get_bot().get_group_list() 94 | res = '' 95 | for each in groups: 96 | res += f'{each["group_id"]}: {each["group_name"]}\n' 97 | res = res.rstrip('\n') 98 | await session.send(res) 99 | 100 | 101 | @on_command('重置插件', permission=SUPERUSER, privileged=True) 102 | @depreciated 103 | async def reset_plugins(session: CommandSession): 104 | from os import path 105 | load_plugins(path.join('.', 'plugins'), 'plugins') 106 | -------------------------------------------------------------------------------- /plugins/repeater/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import asyncio 3 | import random 4 | 5 | from aiocqhttp import Event 6 | from nonebot import get_bot 7 | from nonebot.permission import * 8 | 9 | from utils_bot.msg_ops import msg_is_calling_me 10 | from utils_bot.typing import * 11 | 12 | __plugin_name__ = '复读机 (private)' 13 | __plugin_usage__ = r'''feature: 复读 14 | 人类的本质 15 | ''' 16 | 17 | # acquires global event monitor 18 | bot = get_bot() 19 | 20 | class Record: 21 | def __init__(self, lastMsg: str, count: int): 22 | self.lastMsg = lastMsg 23 | self.count = count 24 | 25 | class Records(dict): 26 | 27 | def get_record(self, group_id: str, msg: str) -> Record: 28 | 'creates tracker for each group at beginning' 29 | record = self.get(group_id) 30 | if record is None: 31 | record = Record(msg, 0) # TODO: better semantics 32 | self[group_id] = record 33 | return record 34 | 35 | def simple_repeat(self, group_id: str, msg: str, 36 | wait_until: int = 3) -> Union[str, None]: 37 | # creates tracker for each group at beginning 38 | record = self.get_record(group_id, msg) 39 | if msg != record.lastMsg: 40 | record.lastMsg, record.count = msg, 1 41 | return 42 | record.count += 1 43 | if record.count == wait_until: 44 | record.count = -999 45 | return msg 46 | 47 | def you_repeat(self, group_id: str, msg: str) -> Union[str, None]: 48 | 'used when message starts with "我"' 49 | record = self.get_record(group_id, msg) 50 | if random.choice((0,0,0,0,1)): 51 | if msg_is_calling_me(msg): 52 | return 53 | record.count = -999 54 | newMsg = [] 55 | for char in msg: 56 | if char == '我': newMsg.append('你') 57 | elif char == '你': newMsg.append('我') 58 | else: newMsg.append(char) 59 | return ''.join(newMsg) 60 | 61 | # key: str 62 | # value: Record 63 | records = Records() 64 | 65 | @bot.on_message('group') 66 | async def _(ctx: Event): 67 | 68 | groupId = ctx['group_id'] 69 | msg = ctx['raw_message'] 70 | 71 | ## special: if message starts with '我' then reply with the same sentece but with '你' 72 | if msg.startswith('我'): 73 | sendMsg = records.you_repeat(groupId, msg) 74 | # delayed 75 | if sendMsg is not None: 76 | await asyncio.sleep(random.randint(1, 10)) 77 | else: 78 | sendMsg = records.simple_repeat(groupId, msg, 3) 79 | if sendMsg is not None: 80 | await bot.send_group_msg(group_id=groupId, message=sendMsg) 81 | -------------------------------------------------------------------------------- /plugins/request_handler.py: -------------------------------------------------------------------------------- 1 | from nonebot import NoticeSession, RequestSession, on_notice, on_request 2 | 3 | from utils_bot.msg_ops import SUPERUSERS, send_to_superusers 4 | 5 | __plugin_usage__ = r'''feature: 处理群消息 6 | ''' 7 | 8 | 9 | 10 | # accepts group invites only from superusers, instantly 11 | @on_request('group.invite') 12 | async def _(session: RequestSession): 13 | sender = session.event['user_id'] 14 | if sender in SUPERUSERS: 15 | await session.approve() 16 | await send_to_superusers( 17 | f'接受了群{session.event["group_id"]}的邀请。(发起人:{sender})') 18 | else: 19 | await session.reject() 20 | 21 | 22 | # notify superusers when bot joins a group 23 | @on_notice('group_increase.invite') 24 | async def _(session: NoticeSession): 25 | if session.event['self_id'] != session.event['user_id']: 26 | return 27 | await send_to_superusers( 28 | f'加入了群{session.event["group_id"]}。') 29 | 30 | 31 | # notify superusers when bot gets kicked from a group 32 | @on_notice('group_decrease.kick_me') 33 | async def _(session: NoticeSession): 34 | if session.event['self_id'] != session.event['user_id']: 35 | return 36 | await send_to_superusers( 37 | f'被群{session.event["group_id"]}踢出了。执行者:{session.event["operator_id"]}') 38 | 39 | 40 | # notify superusers when bot is set admin 41 | @on_notice('group_admin.set') 42 | async def _(session: NoticeSession): 43 | if session.event['self_id'] != session.event['user_id']: 44 | return 45 | await send_to_superusers( 46 | f'被群{session.event["group_id"]}设置为管理员。') 47 | 48 | 49 | # notify superusers when bot is unset admin 50 | @on_notice('group_admin.unset') 51 | async def _(session: NoticeSession): 52 | if session.event['self_id'] != session.event['user_id']: 53 | return 54 | await send_to_superusers( 55 | f'被群{session.event["group_id"]}取消管理员。') 56 | -------------------------------------------------------------------------------- /plugins/song/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import (CommandSession, IntentCommand, MessageSegment, NLPSession, 2 | on_command, on_natural_language) 3 | from nonebot.permission import * 4 | 5 | from utils_bot.command_ops import global_cooldown 6 | from utils_bot.logging import logger 7 | 8 | from .data_source import get_netease_song_id 9 | 10 | __plugin_name__ = '点歌' 11 | __plugin_usage__ = r'''feature: 从网易云分享一首歌曲~ 12 | 13 | 点歌 [歌名] 点一首歌 14 | ''' 15 | 16 | 17 | @on_command('点歌', permission=SUPERUSER | GROUP_MEMBER) 18 | @global_cooldown(1) 19 | async def share_song(session: CommandSession): 20 | songname: str = session.get('songname') 21 | songid = await get_netease_song_id(songname) 22 | report = MessageSegment.music('163', songid) if songid is not None \ 23 | else '未能找到这首歌曲?~' 24 | await session.send(report) 25 | logger.info(f'share song called: {report}...') 26 | 27 | @share_song.args_parser 28 | async def _(session: CommandSession): 29 | argStripped: str = session.current_arg_text.strip() 30 | 31 | if session.is_first_run: 32 | if argStripped: 33 | session.state['songname'] = argStripped 34 | else: 35 | session.finish(__plugin_usage__) 36 | 37 | # also match "点歌xxx" without space in between 38 | @on_natural_language(keywords={'点歌'}, permission=SUPERUSER | GROUP_MEMBER) 39 | async def _(session: NLPSession): 40 | if session.msg_text[:2] == '点歌': 41 | return IntentCommand(64.0, '点歌', current_arg=session.msg_text[2:]) 42 | return IntentCommand(0, '点歌') 43 | -------------------------------------------------------------------------------- /plugins/song/data_source.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import quote_plus 2 | 3 | import aiohttp 4 | 5 | from utils_bot.logging import logger 6 | from utils_bot.typing import Union 7 | 8 | # source: https://github.com/mixmoe/HibiAPI 9 | 10 | URL_FORMAT = 'https://api.obfs.dev/api/netease/search?s={}&search_type=1&limit=1&offset=0' 11 | 12 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'} 13 | 14 | async def get_netease_song_id(name: str) -> Union[int, None]: 15 | try: 16 | async with aiohttp.ClientSession() as session: 17 | res = await session.get(URL_FORMAT.format(quote_plus(name)), headers=headers, timeout=10) 18 | res.raise_for_status() 19 | jsn = await res.json() 20 | return jsn['result']['songs'][0]['id'] 21 | except Exception as e: 22 | logger.exception(e) 23 | return None 24 | -------------------------------------------------------------------------------- /plugins/test_rp/__init__.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from nonebot import CommandSession, on_command 4 | from nonebot.permission import * 5 | 6 | from utils_bot.datetime import TZ, datetime 7 | 8 | __plugin_name__ = '今日运气' 9 | __plugin_usage__ = f'''feature: 看看今天的运气~ 10 | 可用命令:运气 11 | 不为结果负责。 12 | ''' 13 | 14 | class TestLuck: 15 | 16 | @staticmethod 17 | def return_luck_by_num(num: float) -> str: 18 | if 0.0 <= num < 0.1: 19 | return '极坏' 20 | elif 0.1 <= num < 0.4: 21 | return '坏' 22 | elif 0.4 <= num < 0.7: 23 | return '一般' 24 | elif 0.7 <= num < 0.9: 25 | return '好' 26 | else: 27 | return '极好' 28 | 29 | @classmethod 30 | def generate_luck_result(cls, sender_id: int) -> str: 31 | senderId: int = sender_id 32 | timeStamp: int = int( 33 | datetime.now(TZ).strftime('%m%d%Y') 34 | ) 35 | seed: int = (senderId * 2) | (timeStamp * 333) 36 | random.seed(seed) 37 | res: float = random.random() 38 | random.seed() 39 | return cls.return_luck_by_num(res) 40 | 41 | @on_command('我的运气', aliases=('运气', '今日运气', '今日人品', '我的人品', 'jrrp', '运势', '今日运势'), permission=GROUP_MEMBER | SUPERUSER) 42 | async def my_luck_today(session: CommandSession): 43 | senderId: int = int(session.event['user_id']) 44 | await session.send(TestLuck.generate_luck_result(senderId), at_sender=True) 45 | -------------------------------------------------------------------------------- /plugins/usage.py: -------------------------------------------------------------------------------- 1 | import nonebot 2 | from nonebot import on_command, CommandSession, get_bot 3 | from nonebot.permission import * 4 | 5 | __plugin_name__ = '帮助' 6 | __plugin_usage__ = r'''feature: 帮助 7 | 8 | 帮助 获取帮助 9 | 参数: 10 | [功能名] 获取特定功能的帮助 11 | 例子: 12 | 千堂帮助 google 13 | ''' 14 | 15 | @on_command('帮助', aliases=['help', '功能'], permission=SUPERUSER | GROUP_MEMBER) 16 | async def _(session: CommandSession): 17 | # get list of all plugins 18 | if session.event['user_id'] in get_bot().config.SUPERUSERS\ 19 | and not session.event.get('group_id') \ 20 | and not session.event.get('discuss_id'): 21 | plugins = [p for p in nonebot.get_loaded_plugins() if p.name] 22 | else: 23 | plugins = [p for p in nonebot.get_loaded_plugins() if p.name\ 24 | and not p.name.endswith('private)')] 25 | 26 | arg = session.current_arg_text.strip().lower() 27 | if not arg: 28 | text = '呼叫我的名字来使用以下功能~:\n{\n\t' +\ 29 | '\n\t'.join(p.name or '(unnamed)' for p in plugins) + '\n}' +\ 30 | '例子:帮助 天气' 31 | await session.send(text) 32 | 33 | else: 34 | for p in plugins: 35 | if arg in p.name.lower(): 36 | await session.send(p.usage.strip()) 37 | -------------------------------------------------------------------------------- /plugins/weather/__init__.py: -------------------------------------------------------------------------------- 1 | from jieba import posseg 2 | from nonebot import (CommandSession, IntentCommand, NLPSession, 3 | on_command, on_natural_language) 4 | from nonebot.permission import * 5 | from translate import Translator 6 | 7 | from utils_bot.logging import logger 8 | 9 | from .china_city_list_source_amap import CHINESE_CITIES 10 | from .data_source_amap import amap_weather 11 | from .data_source_openweather import openweathermap_weather 12 | 13 | __plugin_name__ = '天气 *NL' 14 | __plugin_usage__ = r'''feature: 查询天气 15 | 16 | 天气 [城市] 获取天气预报 17 | 来源: 18 | CN: AMAP, https://www.amap.com/ 19 | EN: openweathermap http://api.openweathermap.org/ 20 | ''' 21 | 22 | class util: 23 | @staticmethod 24 | def isChinese(word: str) -> bool: 25 | for ch in word: 26 | if '\u4e00' <= ch <= '\u9fff': 27 | return True 28 | return False 29 | 30 | @staticmethod 31 | def isAmapSupported(word: str) -> bool: 32 | return word.rstrip('市') in CHINESE_CITIES 33 | 34 | @staticmethod 35 | def translate2Eng(word: str) -> str: 36 | # LOW EFFICIENCY MANUAL CORRECTION 37 | if word == '雪城': return 'syracuse' 38 | 39 | translator = Translator(from_lang='zh', to_lang='en') 40 | res = translator.translate(word).lower().rstrip(' city') 41 | return res 42 | 43 | 44 | @on_command('weather', aliases=('天气', '天气查询', '查询天气', '查天气'), permission=SUPERUSER | GROUP_MEMBER) 45 | async def weather(session: CommandSession): 46 | city: str = session.get('city') 47 | isChinese: bool = util.isChinese(city) 48 | 49 | # AMAP 50 | if isChinese and util.isAmapSupported(city): 51 | weatherReport = await amap_weather(city) 52 | # OpenWeatherMap (delete block if you do not need) 53 | else: 54 | # openweathermap needs an English city name 55 | city = util.translate2Eng(city) if isChinese else city 56 | weatherReport = await openweathermap_weather(city) 57 | 58 | await session.send(weatherReport) 59 | logger.info(f'weather called: {weatherReport[:100]}...') 60 | 61 | @weather.args_parser 62 | async def _(session: CommandSession): 63 | argStripped: str = session.current_arg_text.strip() 64 | 65 | if session.is_first_run: 66 | if argStripped: 67 | session.state['city'] = argStripped 68 | else: 69 | session.finish(__plugin_usage__) 70 | # EXP 71 | @on_natural_language(keywords={'天气'}, permission=SUPERUSER | GROUP_MEMBER) 72 | async def _(session: NLPSession): 73 | argsStripped = session.msg_text.strip() 74 | words = list(posseg.lcut(argsStripped)) 75 | 76 | city = None 77 | for word in words: 78 | if word.flag == 'ns': 79 | city = word.word 80 | break 81 | if city is None: 82 | city = ' '.join((word.word for word in words if word.flag == 'eng')) 83 | 84 | return IntentCommand(64.0, 'weather', current_arg=city or '') 85 | -------------------------------------------------------------------------------- /plugins/weather/china_city_list_source_amap.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | 3 | with open(path.join(path.dirname(__file__), 'china_city_list_source_amap.txt'), 'r', encoding='utf-8') as f: 4 | CHINESE_CITIES = { line.strip('\n') for line in f } 5 | -------------------------------------------------------------------------------- /plugins/weather/china_city_list_source_amap.txt: -------------------------------------------------------------------------------- 1 | 中华人民共和国 2 | 北京 3 | 北京 4 | 东城 5 | 西城 6 | 朝阳 7 | 丰台 8 | 石景山 9 | 海淀 10 | 门头沟 11 | 房山 12 | 通州 13 | 顺义 14 | 昌平 15 | 大兴 16 | 怀柔 17 | 平谷 18 | 密云 19 | 延庆 20 | 天津 21 | 天津 22 | 和平 23 | 河东 24 | 河西 25 | 南开 26 | 河北 27 | 红桥 28 | 东丽 29 | 西青 30 | 津南 31 | 北辰 32 | 武清 33 | 宝坻 34 | 滨海新 35 | 宁河 36 | 静海 37 | 蓟州 38 | 河北省 39 | 石家庄 40 | 石家庄 41 | 长安 42 | 桥西 43 | 新华 44 | 井陉矿 45 | 裕华 46 | 藁城 47 | 鹿泉 48 | 栾城 49 | 井陉 50 | 正定 51 | 行唐 52 | 灵寿 53 | 高邑 54 | 深泽 55 | 赞皇 56 | 无极 57 | 平山 58 | 元氏 59 | 赵 60 | 辛集 61 | 晋州 62 | 新乐 63 | 唐山 64 | 唐山 65 | 路南 66 | 路北 67 | 古冶 68 | 开平 69 | 丰南 70 | 丰润 71 | 曹妃甸 72 | 滦州 73 | 滦南 74 | 乐亭 75 | 迁西 76 | 玉田 77 | 遵化 78 | 迁安 79 | 秦皇岛 80 | 秦皇岛 81 | 海港 82 | 山海关 83 | 北戴河 84 | 抚宁 85 | 青龙满族 86 | 昌黎 87 | 卢龙 88 | 邯郸 89 | 邯郸 90 | 邯山 91 | 丛台 92 | 复兴 93 | 峰峰矿 94 | 肥乡 95 | 永年 96 | 临漳 97 | 成安 98 | 大名 99 | 涉 100 | 磁 101 | 邱 102 | 鸡泽 103 | 广平 104 | 馆陶 105 | 魏 106 | 曲周 107 | 武安 108 | 邢台 109 | 邢台 110 | 桥东 111 | 桥西 112 | 邢台 113 | 临城 114 | 内丘 115 | 柏乡 116 | 隆尧 117 | 任 118 | 南和 119 | 宁晋 120 | 巨鹿 121 | 新河 122 | 广宗 123 | 平乡 124 | 威 125 | 清河 126 | 临西 127 | 南宫 128 | 沙河 129 | 保定 130 | 保定 131 | 竞秀 132 | 莲池 133 | 满城 134 | 清苑 135 | 徐水 136 | 涞水 137 | 阜平 138 | 定兴 139 | 唐 140 | 高阳 141 | 容城 142 | 涞源 143 | 望都 144 | 安新 145 | 易 146 | 曲阳 147 | 蠡 148 | 顺平 149 | 博野 150 | 雄 151 | 涿州 152 | 定州 153 | 安国 154 | 高碑店 155 | 张家口 156 | 张家口 157 | 桥东 158 | 桥西 159 | 宣化 160 | 下花园 161 | 万全 162 | 崇礼 163 | 张北 164 | 康保 165 | 沽源 166 | 尚义 167 | 蔚 168 | 阳原 169 | 怀安 170 | 怀来 171 | 涿鹿 172 | 赤城 173 | 承德 174 | 承德 175 | 双桥 176 | 双滦 177 | 鹰手营子矿 178 | 承德 179 | 兴隆 180 | 滦平 181 | 隆化 182 | 丰宁满族 183 | 宽城满族 184 | 围场满族蒙古族 185 | 平泉 186 | 沧州 187 | 沧州 188 | 新华 189 | 运河 190 | 沧 191 | 青 192 | 东光 193 | 海兴 194 | 盐山 195 | 肃宁 196 | 南皮 197 | 吴桥 198 | 献 199 | 孟村回族 200 | 泊头 201 | 任丘 202 | 黄骅 203 | 河间 204 | 廊坊 205 | 廊坊 206 | 安次 207 | 广阳 208 | 固安 209 | 永清 210 | 香河 211 | 大城 212 | 文安 213 | 大厂回族 214 | 霸州 215 | 三河 216 | 衡水 217 | 衡水 218 | 桃城 219 | 冀州 220 | 枣强 221 | 武邑 222 | 武强 223 | 饶阳 224 | 安平 225 | 故城 226 | 景 227 | 阜城 228 | 深州 229 | 山西省 230 | 太原 231 | 太原 232 | 小店 233 | 迎泽 234 | 杏花岭 235 | 尖草坪 236 | 万柏林 237 | 晋源 238 | 清徐 239 | 阳曲 240 | 娄烦 241 | 古交 242 | 大同 243 | 大同 244 | 平城 245 | 云冈 246 | 新荣 247 | 阳高 248 | 天镇 249 | 广灵 250 | 灵丘 251 | 浑源 252 | 左云 253 | 云州 254 | 阳泉 255 | 阳泉 256 | 城 257 | 矿 258 | 郊 259 | 平定 260 | 盂 261 | 长治 262 | 长治 263 | 潞州 264 | 上党 265 | 襄垣 266 | 屯留 267 | 平顺 268 | 黎城 269 | 壶关 270 | 长子 271 | 武乡 272 | 沁 273 | 沁源 274 | 潞城 275 | 晋城 276 | 晋城 277 | 城 278 | 沁水 279 | 阳城 280 | 陵川 281 | 泽州 282 | 高平 283 | 朔州 284 | 朔州 285 | 朔城 286 | 平鲁 287 | 山阴 288 | 应 289 | 右玉 290 | 怀仁 291 | 晋中 292 | 晋中 293 | 榆次 294 | 榆社 295 | 左权 296 | 和顺 297 | 昔阳 298 | 寿阳 299 | 太谷 300 | 祁 301 | 平遥 302 | 灵石 303 | 介休 304 | 运城 305 | 运城 306 | 盐湖 307 | 临猗 308 | 万荣 309 | 闻喜 310 | 稷山 311 | 新绛 312 | 绛 313 | 垣曲 314 | 夏 315 | 平陆 316 | 芮城 317 | 永济 318 | 河津 319 | 忻州 320 | 忻州 321 | 忻府 322 | 定襄 323 | 五台 324 | 代 325 | 繁峙 326 | 宁武 327 | 静乐 328 | 神池 329 | 五寨 330 | 岢岚 331 | 河曲 332 | 保德 333 | 偏关 334 | 原平 335 | 临汾 336 | 临汾 337 | 尧都 338 | 曲沃 339 | 翼城 340 | 襄汾 341 | 洪洞 342 | 古 343 | 安泽 344 | 浮山 345 | 吉 346 | 乡宁 347 | 大宁 348 | 隰 349 | 永和 350 | 蒲 351 | 汾西 352 | 侯马 353 | 霍州 354 | 吕梁 355 | 吕梁 356 | 离石 357 | 文水 358 | 交城 359 | 兴 360 | 临 361 | 柳林 362 | 石楼 363 | 岚 364 | 方山 365 | 中阳 366 | 交口 367 | 孝义 368 | 汾阳 369 | 内蒙古 370 | 呼和浩特 371 | 呼和浩特 372 | 新城 373 | 回民 374 | 玉泉 375 | 赛罕 376 | 土默特左旗 377 | 托克托 378 | 和林格尔 379 | 清水河 380 | 武川 381 | 包头 382 | 包头 383 | 东河 384 | 昆都仑 385 | 青山 386 | 石拐 387 | 白云鄂博矿 388 | 九原 389 | 土默特右旗 390 | 固阳 391 | 达尔罕茂明安联合旗 392 | 乌海 393 | 乌海 394 | 海勃湾 395 | 海南 396 | 乌达 397 | 赤峰 398 | 赤峰 399 | 红山 400 | 元宝山 401 | 松山 402 | 阿鲁科尔沁旗 403 | 巴林左旗 404 | 巴林右旗 405 | 林西 406 | 克什克腾旗 407 | 翁牛特旗 408 | 喀喇沁旗 409 | 宁城 410 | 敖汉旗 411 | 通辽 412 | 通辽 413 | 科尔沁 414 | 科尔沁左翼中旗 415 | 科尔沁左翼后旗 416 | 开鲁 417 | 库伦旗 418 | 奈曼旗 419 | 扎鲁特旗 420 | 霍林郭勒 421 | 鄂尔多斯 422 | 鄂尔多斯 423 | 东胜 424 | 康巴什 425 | 达拉特旗 426 | 准格尔旗 427 | 鄂托克前旗 428 | 鄂托克旗 429 | 杭锦旗 430 | 乌审旗 431 | 伊金霍洛旗 432 | 呼伦贝尔 433 | 呼伦贝尔 434 | 海拉尔 435 | 扎赉诺尔 436 | 阿荣旗 437 | 莫力达瓦达斡尔族旗 438 | 鄂伦春旗 439 | 鄂温克族旗 440 | 陈巴尔虎旗 441 | 新巴尔虎左旗 442 | 新巴尔虎右旗 443 | 满洲里 444 | 牙克石 445 | 扎兰屯 446 | 额尔古纳 447 | 根河 448 | 巴彦淖尔 449 | 巴彦淖尔 450 | 临河 451 | 五原 452 | 磴口 453 | 乌拉特前旗 454 | 乌拉特中旗 455 | 乌拉特后旗 456 | 杭锦后旗 457 | 乌兰察布 458 | 乌兰察布 459 | 集宁 460 | 卓资 461 | 化德 462 | 商都 463 | 兴和 464 | 凉城 465 | 察哈尔右翼前旗 466 | 察哈尔右翼中旗 467 | 察哈尔右翼后旗 468 | 四子王旗 469 | 丰镇 470 | 兴安盟 471 | 乌兰浩特 472 | 阿尔山 473 | 科尔沁右翼前旗 474 | 科尔沁右翼中旗 475 | 扎赉特旗 476 | 突泉 477 | 锡林郭勒盟 478 | 二连浩特 479 | 锡林浩特 480 | 阿巴嘎旗 481 | 苏尼特左旗 482 | 苏尼特右旗 483 | 东乌珠穆沁旗 484 | 西乌珠穆沁旗 485 | 太仆寺旗 486 | 镶黄旗 487 | 正镶白旗 488 | 正蓝旗 489 | 多伦 490 | 阿拉善盟 491 | 阿拉善左旗 492 | 阿拉善右旗 493 | 额济纳旗 494 | 辽宁省 495 | 沈阳 496 | 沈阳 497 | 和平 498 | 沈河 499 | 大东 500 | 皇姑 501 | 铁西 502 | 苏家屯 503 | 浑南 504 | 沈北新 505 | 于洪 506 | 辽中 507 | 康平 508 | 法库 509 | 新民 510 | 大连 511 | 大连 512 | 中山 513 | 西岗 514 | 沙河口 515 | 甘井子 516 | 旅顺口 517 | 金州 518 | 普兰店 519 | 长海 520 | 瓦房店 521 | 庄河 522 | 鞍山 523 | 鞍山 524 | 铁东 525 | 铁西 526 | 立山 527 | 千山 528 | 台安 529 | 岫岩满族 530 | 海城 531 | 抚顺 532 | 抚顺 533 | 新抚 534 | 东洲 535 | 望花 536 | 顺城 537 | 抚顺 538 | 新宾满族 539 | 清原满族 540 | 本溪 541 | 本溪 542 | 平山 543 | 溪湖 544 | 明山 545 | 南芬 546 | 本溪满族 547 | 桓仁满族 548 | 丹东 549 | 丹东 550 | 元宝 551 | 振兴 552 | 振安 553 | 宽甸满族 554 | 东港 555 | 凤城 556 | 锦州 557 | 锦州 558 | 古塔 559 | 凌河 560 | 太和 561 | 黑山 562 | 义 563 | 凌海 564 | 北镇 565 | 营口 566 | 营口 567 | 站前 568 | 西 569 | 鲅鱼圈 570 | 老边 571 | 盖州 572 | 大石桥 573 | 阜新 574 | 阜新 575 | 海州 576 | 新邱 577 | 太平 578 | 清河门 579 | 细河 580 | 阜新蒙古族 581 | 彰武 582 | 辽阳 583 | 辽阳 584 | 白塔 585 | 文圣 586 | 宏伟 587 | 弓长岭 588 | 太子河 589 | 辽阳 590 | 灯塔 591 | 盘锦 592 | 盘锦 593 | 双台子 594 | 兴隆台 595 | 大洼 596 | 盘山 597 | 铁岭 598 | 铁岭 599 | 银州 600 | 清河 601 | 铁岭 602 | 西丰 603 | 昌图 604 | 调兵山 605 | 开原 606 | 朝阳 607 | 朝阳 608 | 双塔 609 | 龙城 610 | 朝阳 611 | 建平 612 | 喀喇沁左翼蒙古族 613 | 北票 614 | 凌源 615 | 葫芦岛 616 | 葫芦岛 617 | 连山 618 | 龙港 619 | 南票 620 | 绥中 621 | 建昌 622 | 兴城 623 | 吉林省 624 | 长春 625 | 长春 626 | 南关 627 | 宽城 628 | 朝阳 629 | 二道 630 | 绿园 631 | 双阳 632 | 九台 633 | 农安 634 | 榆树 635 | 德惠 636 | 吉林 637 | 吉林 638 | 昌邑 639 | 龙潭 640 | 船营 641 | 丰满 642 | 永吉 643 | 蛟河 644 | 桦甸 645 | 舒兰 646 | 磐石 647 | 四平 648 | 四平 649 | 铁西 650 | 铁东 651 | 梨树 652 | 伊通满族 653 | 公主岭 654 | 双辽 655 | 辽源 656 | 辽源 657 | 龙山 658 | 西安 659 | 东丰 660 | 东辽 661 | 通化 662 | 通化 663 | 东昌 664 | 二道江 665 | 通化 666 | 辉南 667 | 柳河 668 | 梅河口 669 | 集安 670 | 白山 671 | 白山 672 | 浑江 673 | 江源 674 | 抚松 675 | 靖宇 676 | 长白朝鲜族 677 | 临江 678 | 松原 679 | 松原 680 | 宁江 681 | 前郭尔罗斯蒙古族 682 | 长岭 683 | 乾安 684 | 扶余 685 | 白城 686 | 白城 687 | 洮北 688 | 镇赉 689 | 通榆 690 | 洮南 691 | 大安 692 | 延边朝鲜族州 693 | 延吉 694 | 图们 695 | 敦化 696 | 珲春 697 | 龙井 698 | 和龙 699 | 汪清 700 | 安图 701 | 黑龙江省 702 | 哈尔滨 703 | 哈尔滨 704 | 道里 705 | 南岗 706 | 道外 707 | 平房 708 | 松北 709 | 香坊 710 | 呼兰 711 | 阿城 712 | 双城 713 | 依兰 714 | 方正 715 | 宾 716 | 巴彦 717 | 木兰 718 | 通河 719 | 延寿 720 | 尚志 721 | 五常 722 | 齐齐哈尔 723 | 齐齐哈尔 724 | 龙沙 725 | 建华 726 | 铁锋 727 | 昂昂溪 728 | 富拉尔基 729 | 碾子山 730 | 梅里斯达斡尔族 731 | 龙江 732 | 依安 733 | 泰来 734 | 甘南 735 | 富裕 736 | 克山 737 | 克东 738 | 拜泉 739 | 讷河 740 | 鸡西 741 | 鸡西 742 | 鸡冠 743 | 恒山 744 | 滴道 745 | 梨树 746 | 城子河 747 | 麻山 748 | 鸡东 749 | 虎林 750 | 密山 751 | 鹤岗 752 | 鹤岗 753 | 向阳 754 | 工农 755 | 南山 756 | 兴安 757 | 东山 758 | 兴山 759 | 萝北 760 | 绥滨 761 | 双鸭山 762 | 双鸭山 763 | 尖山 764 | 岭东 765 | 四方台 766 | 宝山 767 | 集贤 768 | 友谊 769 | 宝清 770 | 饶河 771 | 大庆 772 | 大庆 773 | 萨尔图 774 | 龙凤 775 | 让胡路 776 | 红岗 777 | 大同 778 | 肇州 779 | 肇源 780 | 林甸 781 | 杜尔伯特蒙古族 782 | 伊春 783 | 伊春 784 | 伊春 785 | 南岔 786 | 友好 787 | 西林 788 | 翠峦 789 | 新青 790 | 美溪 791 | 金山屯 792 | 五营 793 | 乌马河 794 | 汤旺河 795 | 带岭 796 | 乌伊岭 797 | 红星 798 | 上甘岭 799 | 嘉荫 800 | 铁力 801 | 佳木斯 802 | 佳木斯 803 | 向阳 804 | 前进 805 | 东风 806 | 郊 807 | 桦南 808 | 桦川 809 | 汤原 810 | 同江 811 | 富锦 812 | 抚远 813 | 七台河 814 | 七台河 815 | 新兴 816 | 桃山 817 | 茄子河 818 | 勃利 819 | 牡丹江 820 | 牡丹江 821 | 东安 822 | 阳明 823 | 爱民 824 | 西安 825 | 林口 826 | 绥芬河 827 | 海林 828 | 宁安 829 | 穆棱 830 | 东宁 831 | 黑河 832 | 黑河 833 | 爱辉 834 | 嫩江 835 | 逊克 836 | 孙吴 837 | 北安 838 | 五大连池 839 | 绥化 840 | 绥化 841 | 北林 842 | 望奎 843 | 兰西 844 | 青冈 845 | 庆安 846 | 明水 847 | 绥棱 848 | 安达 849 | 肇东 850 | 海伦 851 | 大兴安岭地 852 | 加格达奇 853 | 呼玛 854 | 塔河 855 | 漠河 856 | 上海 857 | 上海 858 | 黄浦 859 | 徐汇 860 | 长宁 861 | 静安 862 | 普陀 863 | 虹口 864 | 杨浦 865 | 闵行 866 | 宝山 867 | 嘉定 868 | 浦东新 869 | 金山 870 | 松江 871 | 青浦 872 | 奉贤 873 | 崇明 874 | 江苏省 875 | 南京 876 | 南京 877 | 玄武 878 | 秦淮 879 | 建邺 880 | 鼓楼 881 | 浦口 882 | 栖霞 883 | 雨花台 884 | 江宁 885 | 六合 886 | 溧水 887 | 高淳 888 | 无锡 889 | 无锡 890 | 锡山 891 | 惠山 892 | 滨湖 893 | 梁溪 894 | 新吴 895 | 江阴 896 | 宜兴 897 | 徐州 898 | 徐州 899 | 鼓楼 900 | 云龙 901 | 贾汪 902 | 泉山 903 | 铜山 904 | 丰 905 | 沛 906 | 睢宁 907 | 新沂 908 | 邳州 909 | 常州 910 | 常州 911 | 天宁 912 | 钟楼 913 | 新北 914 | 武进 915 | 金坛 916 | 溧阳 917 | 苏州 918 | 苏州 919 | 虎丘 920 | 吴中 921 | 相城 922 | 姑苏 923 | 吴江 924 | 苏州工业园 925 | 常熟 926 | 张家港 927 | 昆山 928 | 太仓 929 | 南通 930 | 南通 931 | 崇川 932 | 港闸 933 | 通州 934 | 海安 935 | 如东 936 | 启东 937 | 如皋 938 | 海门 939 | 连云港 940 | 连云港 941 | 连云 942 | 海州 943 | 赣榆 944 | 东海 945 | 灌云 946 | 灌南 947 | 淮安 948 | 淮安 949 | 淮安 950 | 淮阴 951 | 清江浦 952 | 洪泽 953 | 涟水 954 | 盱眙 955 | 金湖 956 | 盐城 957 | 盐城 958 | 亭湖 959 | 盐都 960 | 大丰 961 | 响水 962 | 滨海 963 | 阜宁 964 | 射阳 965 | 建湖 966 | 东台 967 | 扬州 968 | 扬州 969 | 广陵 970 | 邗江 971 | 江都 972 | 宝应 973 | 仪征 974 | 高邮 975 | 镇江 976 | 镇江 977 | 京口 978 | 润州 979 | 丹徒 980 | 丹阳 981 | 扬中 982 | 句容 983 | 泰州 984 | 泰州 985 | 海陵 986 | 高港 987 | 姜堰 988 | 兴化 989 | 靖江 990 | 泰兴 991 | 宿迁 992 | 宿迁 993 | 宿城 994 | 宿豫 995 | 沭阳 996 | 泗阳 997 | 泗洪 998 | 浙江省 999 | 杭州 1000 | 杭州 1001 | 上城 1002 | 下城 1003 | 江干 1004 | 拱墅 1005 | 西湖 1006 | 滨江 1007 | 萧山 1008 | 余杭 1009 | 富阳 1010 | 临安 1011 | 桐庐 1012 | 淳安 1013 | 建德 1014 | 宁波 1015 | 宁波 1016 | 海曙 1017 | 江北 1018 | 北仑 1019 | 镇海 1020 | 鄞州 1021 | 奉化 1022 | 象山 1023 | 宁海 1024 | 余姚 1025 | 慈溪 1026 | 温州 1027 | 温州 1028 | 鹿城 1029 | 龙湾 1030 | 瓯海 1031 | 洞头 1032 | 永嘉 1033 | 平阳 1034 | 苍南 1035 | 文成 1036 | 泰顺 1037 | 瑞安 1038 | 乐清 1039 | 嘉兴 1040 | 嘉兴 1041 | 南湖 1042 | 秀洲 1043 | 嘉善 1044 | 海盐 1045 | 海宁 1046 | 平湖 1047 | 桐乡 1048 | 湖州 1049 | 湖州 1050 | 吴兴 1051 | 南浔 1052 | 德清 1053 | 长兴 1054 | 安吉 1055 | 绍兴 1056 | 绍兴 1057 | 越城 1058 | 柯桥 1059 | 上虞 1060 | 新昌 1061 | 诸暨 1062 | 嵊州 1063 | 金华 1064 | 金华 1065 | 婺城 1066 | 金东 1067 | 武义 1068 | 浦江 1069 | 磐安 1070 | 兰溪 1071 | 义乌 1072 | 东阳 1073 | 永康 1074 | 衢州 1075 | 衢州 1076 | 柯城 1077 | 衢江 1078 | 常山 1079 | 开化 1080 | 龙游 1081 | 江山 1082 | 舟山 1083 | 舟山 1084 | 定海 1085 | 普陀 1086 | 岱山 1087 | 嵊泗 1088 | 台州 1089 | 台州 1090 | 椒江 1091 | 黄岩 1092 | 路桥 1093 | 三门 1094 | 天台 1095 | 仙居 1096 | 温岭 1097 | 临海 1098 | 玉环 1099 | 丽水 1100 | 丽水 1101 | 莲都 1102 | 青田 1103 | 缙云 1104 | 遂昌 1105 | 松阳 1106 | 云和 1107 | 庆元 1108 | 景宁畲族 1109 | 龙泉 1110 | 安徽省 1111 | 合肥 1112 | 合肥 1113 | 瑶海 1114 | 庐阳 1115 | 蜀山 1116 | 包河 1117 | 长丰 1118 | 肥东 1119 | 肥西 1120 | 庐江 1121 | 巢湖 1122 | 芜湖 1123 | 芜湖 1124 | 镜湖 1125 | 弋江 1126 | 鸠江 1127 | 三山 1128 | 芜湖 1129 | 繁昌 1130 | 南陵 1131 | 无为 1132 | 蚌埠 1133 | 蚌埠 1134 | 龙子湖 1135 | 蚌山 1136 | 禹会 1137 | 淮上 1138 | 怀远 1139 | 五河 1140 | 固镇 1141 | 淮南 1142 | 淮南 1143 | 大通 1144 | 田家庵 1145 | 谢家集 1146 | 八公山 1147 | 潘集 1148 | 凤台 1149 | 寿 1150 | 马鞍山 1151 | 马鞍山 1152 | 花山 1153 | 雨山 1154 | 博望 1155 | 当涂 1156 | 含山 1157 | 和 1158 | 淮北 1159 | 淮北 1160 | 杜集 1161 | 相山 1162 | 烈山 1163 | 濉溪 1164 | 铜陵 1165 | 铜陵 1166 | 铜官 1167 | 义安 1168 | 郊 1169 | 枞阳 1170 | 安庆 1171 | 安庆 1172 | 迎江 1173 | 大观 1174 | 宜秀 1175 | 怀宁 1176 | 潜山 1177 | 太湖 1178 | 宿松 1179 | 望江 1180 | 岳西 1181 | 桐城 1182 | 黄山 1183 | 黄山 1184 | 屯溪 1185 | 黄山 1186 | 徽州 1187 | 歙 1188 | 休宁 1189 | 黟 1190 | 祁门 1191 | 滁州 1192 | 滁州 1193 | 琅琊 1194 | 南谯 1195 | 来安 1196 | 全椒 1197 | 定远 1198 | 凤阳 1199 | 天长 1200 | 明光 1201 | 阜阳 1202 | 阜阳 1203 | 颍州 1204 | 颍东 1205 | 颍泉 1206 | 临泉 1207 | 太和 1208 | 阜南 1209 | 颍上 1210 | 界首 1211 | 宿州 1212 | 宿州 1213 | 埇桥 1214 | 砀山 1215 | 萧 1216 | 灵璧 1217 | 泗 1218 | 六安 1219 | 六安 1220 | 金安 1221 | 裕安 1222 | 叶集 1223 | 霍邱 1224 | 舒城 1225 | 金寨 1226 | 霍山 1227 | 亳州 1228 | 亳州 1229 | 谯城 1230 | 涡阳 1231 | 蒙城 1232 | 利辛 1233 | 池州 1234 | 池州 1235 | 贵池 1236 | 东至 1237 | 石台 1238 | 青阳 1239 | 宣城 1240 | 宣城 1241 | 宣州 1242 | 郎溪 1243 | 广德 1244 | 泾 1245 | 绩溪 1246 | 旌德 1247 | 宁国 1248 | 福建省 1249 | 福州 1250 | 福州 1251 | 鼓楼 1252 | 台江 1253 | 仓山 1254 | 马尾 1255 | 晋安 1256 | 长乐 1257 | 闽侯 1258 | 连江 1259 | 罗源 1260 | 闽清 1261 | 永泰 1262 | 平潭 1263 | 福清 1264 | 厦门 1265 | 厦门 1266 | 思明 1267 | 海沧 1268 | 湖里 1269 | 集美 1270 | 同安 1271 | 翔安 1272 | 莆田 1273 | 莆田 1274 | 城厢 1275 | 涵江 1276 | 荔城 1277 | 秀屿 1278 | 仙游 1279 | 三明 1280 | 三明 1281 | 梅列 1282 | 三元 1283 | 明溪 1284 | 清流 1285 | 宁化 1286 | 大田 1287 | 尤溪 1288 | 沙 1289 | 将乐 1290 | 泰宁 1291 | 建宁 1292 | 永安 1293 | 泉州 1294 | 泉州 1295 | 鲤城 1296 | 丰泽 1297 | 洛江 1298 | 泉港 1299 | 惠安 1300 | 安溪 1301 | 永春 1302 | 德化 1303 | 金门 1304 | 石狮 1305 | 晋江 1306 | 南安 1307 | 漳州 1308 | 漳州 1309 | 芗城 1310 | 龙文 1311 | 云霄 1312 | 漳浦 1313 | 诏安 1314 | 长泰 1315 | 东山 1316 | 南靖 1317 | 平和 1318 | 华安 1319 | 龙海 1320 | 南平 1321 | 南平 1322 | 延平 1323 | 建阳 1324 | 顺昌 1325 | 浦城 1326 | 光泽 1327 | 松溪 1328 | 政和 1329 | 邵武 1330 | 武夷山 1331 | 建瓯 1332 | 龙岩 1333 | 龙岩 1334 | 新罗 1335 | 永定 1336 | 长汀 1337 | 上杭 1338 | 武平 1339 | 连城 1340 | 漳平 1341 | 宁德 1342 | 宁德 1343 | 蕉城 1344 | 霞浦 1345 | 古田 1346 | 屏南 1347 | 寿宁 1348 | 周宁 1349 | 柘荣 1350 | 福安 1351 | 福鼎 1352 | 江西省 1353 | 南昌 1354 | 南昌 1355 | 东湖 1356 | 西湖 1357 | 青云谱 1358 | 湾里 1359 | 青山湖 1360 | 新建 1361 | 南昌 1362 | 安义 1363 | 进贤 1364 | 景德镇 1365 | 景德镇 1366 | 昌江 1367 | 珠山 1368 | 浮梁 1369 | 乐平 1370 | 萍乡 1371 | 萍乡 1372 | 安源 1373 | 湘东 1374 | 莲花 1375 | 上栗 1376 | 芦溪 1377 | 九江 1378 | 九江 1379 | 濂溪 1380 | 浔阳 1381 | 柴桑 1382 | 武宁 1383 | 修水 1384 | 永修 1385 | 德安 1386 | 都昌 1387 | 湖口 1388 | 彭泽 1389 | 瑞昌 1390 | 共青城 1391 | 庐山 1392 | 新余 1393 | 新余 1394 | 渝水 1395 | 分宜 1396 | 鹰潭 1397 | 鹰潭 1398 | 月湖 1399 | 余江 1400 | 贵溪 1401 | 赣州 1402 | 赣州 1403 | 章贡 1404 | 南康 1405 | 赣 1406 | 信丰 1407 | 大余 1408 | 上犹 1409 | 崇义 1410 | 安远 1411 | 龙南 1412 | 定南 1413 | 全南 1414 | 宁都 1415 | 于都 1416 | 兴国 1417 | 会昌 1418 | 寻乌 1419 | 石城 1420 | 瑞金 1421 | 吉安 1422 | 吉安 1423 | 吉州 1424 | 青原 1425 | 吉安 1426 | 吉水 1427 | 峡江 1428 | 新干 1429 | 永丰 1430 | 泰和 1431 | 遂川 1432 | 万安 1433 | 安福 1434 | 永新 1435 | 井冈山 1436 | 宜春 1437 | 宜春 1438 | 袁州 1439 | 奉新 1440 | 万载 1441 | 上高 1442 | 宜丰 1443 | 靖安 1444 | 铜鼓 1445 | 丰城 1446 | 樟树 1447 | 高安 1448 | 抚州 1449 | 抚州 1450 | 临川 1451 | 东乡 1452 | 南城 1453 | 黎川 1454 | 南丰 1455 | 崇仁 1456 | 乐安 1457 | 宜黄 1458 | 金溪 1459 | 资溪 1460 | 广昌 1461 | 上饶 1462 | 上饶 1463 | 信州 1464 | 广丰 1465 | 上饶 1466 | 玉山 1467 | 铅山 1468 | 横峰 1469 | 弋阳 1470 | 余干 1471 | 鄱阳 1472 | 万年 1473 | 婺源 1474 | 德兴 1475 | 山东省 1476 | 济南 1477 | 济南 1478 | 历下 1479 | 中 1480 | 槐荫 1481 | 天桥 1482 | 历城 1483 | 长清 1484 | 章丘 1485 | 平阴 1486 | 济阳 1487 | 商河 1488 | 青岛 1489 | 青岛 1490 | 南 1491 | 北 1492 | 黄岛 1493 | 崂山 1494 | 李沧 1495 | 城阳 1496 | 即墨 1497 | 胶州 1498 | 平度 1499 | 莱西 1500 | 淄博 1501 | 淄博 1502 | 淄川 1503 | 张店 1504 | 博山 1505 | 临淄 1506 | 周村 1507 | 桓台 1508 | 高青 1509 | 沂源 1510 | 枣庄 1511 | 枣庄 1512 | 中 1513 | 薛城 1514 | 峄城 1515 | 台儿庄 1516 | 山亭 1517 | 滕州 1518 | 东营 1519 | 东营 1520 | 东营 1521 | 河口 1522 | 垦利 1523 | 利津 1524 | 广饶 1525 | 烟台 1526 | 烟台 1527 | 芝罘 1528 | 福山 1529 | 牟平 1530 | 莱山 1531 | 长岛 1532 | 龙口 1533 | 莱阳 1534 | 莱州 1535 | 蓬莱 1536 | 招远 1537 | 栖霞 1538 | 海阳 1539 | 潍坊 1540 | 潍坊 1541 | 潍城 1542 | 寒亭 1543 | 坊子 1544 | 奎文 1545 | 临朐 1546 | 昌乐 1547 | 青州 1548 | 诸城 1549 | 寿光 1550 | 安丘 1551 | 高密 1552 | 昌邑 1553 | 济宁 1554 | 济宁 1555 | 任城 1556 | 兖州 1557 | 微山 1558 | 鱼台 1559 | 金乡 1560 | 嘉祥 1561 | 汶上 1562 | 泗水 1563 | 梁山 1564 | 曲阜 1565 | 邹城 1566 | 泰安 1567 | 泰安 1568 | 泰山 1569 | 岱岳 1570 | 宁阳 1571 | 东平 1572 | 新泰 1573 | 肥城 1574 | 威海 1575 | 威海 1576 | 环翠 1577 | 文登 1578 | 荣成 1579 | 乳山 1580 | 日照 1581 | 日照 1582 | 东港 1583 | 岚山 1584 | 五莲 1585 | 莒 1586 | 莱芜 1587 | 钢城 1588 | 临沂 1589 | 临沂 1590 | 兰山 1591 | 罗庄 1592 | 河东 1593 | 沂南 1594 | 郯城 1595 | 沂水 1596 | 兰陵 1597 | 费 1598 | 平邑 1599 | 莒南 1600 | 蒙阴 1601 | 临沭 1602 | 德州 1603 | 德州 1604 | 德城 1605 | 陵城 1606 | 宁津 1607 | 庆云 1608 | 临邑 1609 | 齐河 1610 | 平原 1611 | 夏津 1612 | 武城 1613 | 乐陵 1614 | 禹城 1615 | 聊城 1616 | 聊城 1617 | 东昌府 1618 | 阳谷 1619 | 莘 1620 | 茌平 1621 | 东阿 1622 | 冠 1623 | 高唐 1624 | 临清 1625 | 滨州 1626 | 滨州 1627 | 滨城 1628 | 沾化 1629 | 惠民 1630 | 阳信 1631 | 无棣 1632 | 博兴 1633 | 邹平 1634 | 菏泽 1635 | 菏泽 1636 | 牡丹 1637 | 定陶 1638 | 曹 1639 | 单 1640 | 成武 1641 | 巨野 1642 | 郓城 1643 | 鄄城 1644 | 东明 1645 | 河南省 1646 | 郑州 1647 | 郑州 1648 | 中原 1649 | 二七 1650 | 管城回族 1651 | 金水 1652 | 上街 1653 | 惠济 1654 | 中牟 1655 | 巩义 1656 | 荥阳 1657 | 新密 1658 | 新郑 1659 | 登封 1660 | 开封 1661 | 开封 1662 | 龙亭 1663 | 顺河回族 1664 | 鼓楼 1665 | 禹王台 1666 | 祥符 1667 | 杞 1668 | 通许 1669 | 尉氏 1670 | 兰考 1671 | 洛阳 1672 | 洛阳 1673 | 老城 1674 | 西工 1675 | 瀍河回族 1676 | 涧西 1677 | 吉利 1678 | 洛龙 1679 | 孟津 1680 | 新安 1681 | 栾川 1682 | 嵩 1683 | 汝阳 1684 | 宜阳 1685 | 洛宁 1686 | 伊川 1687 | 偃师 1688 | 平顶山 1689 | 平顶山 1690 | 新华 1691 | 卫东 1692 | 石龙 1693 | 湛河 1694 | 宝丰 1695 | 叶 1696 | 鲁山 1697 | 郏 1698 | 舞钢 1699 | 汝州 1700 | 安阳 1701 | 安阳 1702 | 文峰 1703 | 北关 1704 | 殷都 1705 | 龙安 1706 | 安阳 1707 | 汤阴 1708 | 滑 1709 | 内黄 1710 | 林州 1711 | 鹤壁 1712 | 鹤壁 1713 | 鹤山 1714 | 山城 1715 | 淇滨 1716 | 浚 1717 | 淇 1718 | 新乡 1719 | 新乡 1720 | 红旗 1721 | 卫滨 1722 | 凤泉 1723 | 牧野 1724 | 新乡 1725 | 获嘉 1726 | 原阳 1727 | 延津 1728 | 封丘 1729 | 长垣 1730 | 卫辉 1731 | 辉 1732 | 焦作 1733 | 焦作 1734 | 解放 1735 | 中站 1736 | 马村 1737 | 山阳 1738 | 修武 1739 | 博爱 1740 | 武陟 1741 | 温 1742 | 沁阳 1743 | 孟州 1744 | 濮阳 1745 | 濮阳 1746 | 华龙 1747 | 清丰 1748 | 南乐 1749 | 范 1750 | 台前 1751 | 濮阳 1752 | 许昌 1753 | 许昌 1754 | 魏都 1755 | 建安 1756 | 鄢陵 1757 | 襄城 1758 | 禹州 1759 | 长葛 1760 | 漯河 1761 | 漯河 1762 | 源汇 1763 | 郾城 1764 | 召陵 1765 | 舞阳 1766 | 临颍 1767 | 三门峡 1768 | 三门峡 1769 | 湖滨 1770 | 陕州 1771 | 渑池 1772 | 卢氏 1773 | 义马 1774 | 灵宝 1775 | 南阳 1776 | 南阳 1777 | 宛城 1778 | 卧龙 1779 | 南召 1780 | 方城 1781 | 西峡 1782 | 镇平 1783 | 内乡 1784 | 淅川 1785 | 社旗 1786 | 唐河 1787 | 新野 1788 | 桐柏 1789 | 邓州 1790 | 商丘 1791 | 商丘 1792 | 梁园 1793 | 睢阳 1794 | 民权 1795 | 睢 1796 | 宁陵 1797 | 柘城 1798 | 虞城 1799 | 夏邑 1800 | 永城 1801 | 信阳 1802 | 信阳 1803 | 浉河 1804 | 平桥 1805 | 罗山 1806 | 光山 1807 | 新 1808 | 商城 1809 | 固始 1810 | 潢川 1811 | 淮滨 1812 | 息 1813 | 周口 1814 | 周口 1815 | 川汇 1816 | 扶沟 1817 | 西华 1818 | 商水 1819 | 沈丘 1820 | 郸城 1821 | 淮阳 1822 | 太康 1823 | 鹿邑 1824 | 项城 1825 | 驻马店 1826 | 驻马店 1827 | 驿城 1828 | 西平 1829 | 上蔡 1830 | 平舆 1831 | 正阳 1832 | 确山 1833 | 泌阳 1834 | 汝南 1835 | 遂平 1836 | 新蔡 1837 | 济源 1838 | 湖北省 1839 | 武汉 1840 | 武汉 1841 | 江岸 1842 | 江汉 1843 | 硚口 1844 | 汉阳 1845 | 武昌 1846 | 青山 1847 | 洪山 1848 | 东西湖 1849 | 汉南 1850 | 蔡甸 1851 | 江夏 1852 | 黄陂 1853 | 新洲 1854 | 黄石 1855 | 黄石 1856 | 黄石港 1857 | 西塞山 1858 | 下陆 1859 | 铁山 1860 | 阳新 1861 | 大冶 1862 | 十堰 1863 | 十堰 1864 | 茅箭 1865 | 张湾 1866 | 郧阳 1867 | 郧西 1868 | 竹山 1869 | 竹溪 1870 | 房 1871 | 丹江口 1872 | 宜昌 1873 | 宜昌 1874 | 西陵 1875 | 伍家岗 1876 | 点军 1877 | 猇亭 1878 | 夷陵 1879 | 远安 1880 | 兴山 1881 | 秭归 1882 | 长阳土家族 1883 | 五峰土家族 1884 | 宜都 1885 | 当阳 1886 | 枝江 1887 | 襄阳 1888 | 襄阳 1889 | 襄城 1890 | 樊城 1891 | 襄州 1892 | 南漳 1893 | 谷城 1894 | 保康 1895 | 老河口 1896 | 枣阳 1897 | 宜城 1898 | 鄂州 1899 | 鄂州 1900 | 梁子湖 1901 | 华容 1902 | 鄂城 1903 | 荆门 1904 | 荆门 1905 | 东宝 1906 | 掇刀 1907 | 京山 1908 | 沙洋 1909 | 钟祥 1910 | 孝感 1911 | 孝感 1912 | 孝南 1913 | 孝昌 1914 | 大悟 1915 | 云梦 1916 | 应城 1917 | 安陆 1918 | 汉川 1919 | 荆州 1920 | 荆州 1921 | 沙 1922 | 荆州 1923 | 公安 1924 | 监利 1925 | 江陵 1926 | 石首 1927 | 洪湖 1928 | 松滋 1929 | 黄冈 1930 | 黄冈 1931 | 黄州 1932 | 团风 1933 | 红安 1934 | 罗田 1935 | 英山 1936 | 浠水 1937 | 蕲春 1938 | 黄梅 1939 | 麻城 1940 | 武穴 1941 | 咸宁 1942 | 咸宁 1943 | 咸安 1944 | 嘉鱼 1945 | 通城 1946 | 崇阳 1947 | 通山 1948 | 赤壁 1949 | 随州 1950 | 随州 1951 | 曾都 1952 | 随 1953 | 广水 1954 | 恩施土家族苗族州 1955 | 恩施 1956 | 利川 1957 | 建始 1958 | 巴东 1959 | 宣恩 1960 | 咸丰 1961 | 来凤 1962 | 鹤峰 1963 | 仙桃 1964 | 潜江 1965 | 天门 1966 | 神农架林 1967 | 湖南省 1968 | 长沙 1969 | 长沙 1970 | 芙蓉 1971 | 天心 1972 | 岳麓 1973 | 开福 1974 | 雨花 1975 | 望城 1976 | 长沙 1977 | 浏阳 1978 | 宁乡 1979 | 株洲 1980 | 株洲 1981 | 荷塘 1982 | 芦淞 1983 | 石峰 1984 | 天元 1985 | 渌口 1986 | 攸 1987 | 茶陵 1988 | 炎陵 1989 | 醴陵 1990 | 湘潭 1991 | 湘潭 1992 | 雨湖 1993 | 岳塘 1994 | 湘潭 1995 | 湘乡 1996 | 韶山 1997 | 衡阳 1998 | 衡阳 1999 | 珠晖 2000 | 雁峰 2001 | 石鼓 2002 | 蒸湘 2003 | 南岳 2004 | 衡阳 2005 | 衡南 2006 | 衡山 2007 | 衡东 2008 | 祁东 2009 | 耒阳 2010 | 常宁 2011 | 邵阳 2012 | 邵阳 2013 | 双清 2014 | 大祥 2015 | 北塔 2016 | 邵东 2017 | 新邵 2018 | 邵阳 2019 | 隆回 2020 | 洞口 2021 | 绥宁 2022 | 新宁 2023 | 城步苗族 2024 | 武冈 2025 | 岳阳 2026 | 岳阳 2027 | 岳阳楼 2028 | 云溪 2029 | 君山 2030 | 岳阳 2031 | 华容 2032 | 湘阴 2033 | 平江 2034 | 汨罗 2035 | 临湘 2036 | 常德 2037 | 常德 2038 | 武陵 2039 | 鼎城 2040 | 安乡 2041 | 汉寿 2042 | 澧 2043 | 临澧 2044 | 桃源 2045 | 石门 2046 | 津 2047 | 张家界 2048 | 张家界 2049 | 永定 2050 | 武陵源 2051 | 慈利 2052 | 桑植 2053 | 益阳 2054 | 益阳 2055 | 资阳 2056 | 赫山 2057 | 南 2058 | 桃江 2059 | 安化 2060 | 沅江 2061 | 郴州 2062 | 郴州 2063 | 北湖 2064 | 苏仙 2065 | 桂阳 2066 | 宜章 2067 | 永兴 2068 | 嘉禾 2069 | 临武 2070 | 汝城 2071 | 桂东 2072 | 安仁 2073 | 资兴 2074 | 永州 2075 | 永州 2076 | 零陵 2077 | 冷水滩 2078 | 祁阳 2079 | 东安 2080 | 双牌 2081 | 道 2082 | 江永 2083 | 宁远 2084 | 蓝山 2085 | 新田 2086 | 江华瑶族 2087 | 怀化 2088 | 怀化 2089 | 鹤城 2090 | 中方 2091 | 沅陵 2092 | 辰溪 2093 | 溆浦 2094 | 会同 2095 | 麻阳苗族 2096 | 新晃侗族 2097 | 芷江侗族 2098 | 靖州苗族侗族 2099 | 通道侗族 2100 | 洪江 2101 | 娄底 2102 | 娄底 2103 | 娄星 2104 | 双峰 2105 | 新化 2106 | 冷水江 2107 | 涟源 2108 | 湘西土家族苗族州 2109 | 吉首 2110 | 泸溪 2111 | 凤凰 2112 | 花垣 2113 | 保靖 2114 | 古丈 2115 | 永顺 2116 | 龙山 2117 | 广东省 2118 | 广州 2119 | 广州 2120 | 荔湾 2121 | 越秀 2122 | 海珠 2123 | 天河 2124 | 白云 2125 | 黄埔 2126 | 番禺 2127 | 花都 2128 | 南沙 2129 | 从化 2130 | 增城 2131 | 韶关 2132 | 韶关 2133 | 武江 2134 | 浈江 2135 | 曲江 2136 | 始兴 2137 | 仁化 2138 | 翁源 2139 | 乳源瑶族 2140 | 新丰 2141 | 乐昌 2142 | 南雄 2143 | 深圳 2144 | 深圳 2145 | 罗湖 2146 | 福田 2147 | 南山 2148 | 宝安 2149 | 龙岗 2150 | 盐田 2151 | 龙华 2152 | 坪山 2153 | 光明 2154 | 珠海 2155 | 珠海 2156 | 香洲 2157 | 斗门 2158 | 金湾 2159 | 汕头 2160 | 汕头 2161 | 龙湖 2162 | 金平 2163 | 濠江 2164 | 潮阳 2165 | 潮南 2166 | 澄海 2167 | 南澳 2168 | 佛山 2169 | 佛山 2170 | 禅城 2171 | 南海 2172 | 顺德 2173 | 三水 2174 | 高明 2175 | 江门 2176 | 江门 2177 | 蓬江 2178 | 江海 2179 | 新会 2180 | 台山 2181 | 开平 2182 | 鹤山 2183 | 恩平 2184 | 湛江 2185 | 湛江 2186 | 赤坎 2187 | 霞山 2188 | 坡头 2189 | 麻章 2190 | 遂溪 2191 | 徐闻 2192 | 廉江 2193 | 雷州 2194 | 吴川 2195 | 茂名 2196 | 茂名 2197 | 茂南 2198 | 电白 2199 | 高州 2200 | 化州 2201 | 信宜 2202 | 肇庆 2203 | 肇庆 2204 | 端州 2205 | 鼎湖 2206 | 高要 2207 | 广宁 2208 | 怀集 2209 | 封开 2210 | 德庆 2211 | 四会 2212 | 惠州 2213 | 惠州 2214 | 惠城 2215 | 惠阳 2216 | 博罗 2217 | 惠东 2218 | 龙门 2219 | 梅州 2220 | 梅州 2221 | 梅江 2222 | 梅 2223 | 大埔 2224 | 丰顺 2225 | 五华 2226 | 平远 2227 | 蕉岭 2228 | 兴宁 2229 | 汕尾 2230 | 汕尾 2231 | 城 2232 | 海丰 2233 | 陆河 2234 | 陆丰 2235 | 河源 2236 | 河源 2237 | 源城 2238 | 紫金 2239 | 龙川 2240 | 连平 2241 | 和平 2242 | 东源 2243 | 阳江 2244 | 阳江 2245 | 江城 2246 | 阳东 2247 | 阳西 2248 | 阳春 2249 | 清远 2250 | 清远 2251 | 清城 2252 | 清新 2253 | 佛冈 2254 | 阳山 2255 | 连山壮族瑶族 2256 | 连南瑶族 2257 | 英德 2258 | 连州 2259 | 东莞 2260 | 中山 2261 | 东沙群岛 2262 | 潮州 2263 | 潮州 2264 | 湘桥 2265 | 潮安 2266 | 饶平 2267 | 揭阳 2268 | 揭阳 2269 | 榕城 2270 | 揭东 2271 | 揭西 2272 | 惠来 2273 | 普宁 2274 | 云浮 2275 | 云浮 2276 | 云城 2277 | 云安 2278 | 新兴 2279 | 郁南 2280 | 罗定 2281 | 广西壮族 2282 | 南宁 2283 | 南宁 2284 | 兴宁 2285 | 青秀 2286 | 江南 2287 | 西乡塘 2288 | 良庆 2289 | 邕宁 2290 | 武鸣 2291 | 隆安 2292 | 马山 2293 | 上林 2294 | 宾阳 2295 | 横 2296 | 柳州 2297 | 柳州 2298 | 城中 2299 | 鱼峰 2300 | 柳南 2301 | 柳北 2302 | 柳江 2303 | 柳城 2304 | 鹿寨 2305 | 融安 2306 | 融水苗族 2307 | 三江侗族 2308 | 桂林 2309 | 桂林 2310 | 秀峰 2311 | 叠彩 2312 | 象山 2313 | 七星 2314 | 雁山 2315 | 临桂 2316 | 阳朔 2317 | 灵川 2318 | 全州 2319 | 兴安 2320 | 永福 2321 | 灌阳 2322 | 龙胜各族 2323 | 资源 2324 | 平乐 2325 | 荔浦 2326 | 恭城瑶族 2327 | 梧州 2328 | 梧州 2329 | 万秀 2330 | 长洲 2331 | 龙圩 2332 | 苍梧 2333 | 藤 2334 | 蒙山 2335 | 岑溪 2336 | 北海 2337 | 北海 2338 | 海城 2339 | 银海 2340 | 铁山港 2341 | 合浦 2342 | 防城港 2343 | 防城港 2344 | 港口 2345 | 防城 2346 | 上思 2347 | 东兴 2348 | 钦州 2349 | 钦州 2350 | 钦南 2351 | 钦北 2352 | 灵山 2353 | 浦北 2354 | 贵港 2355 | 贵港 2356 | 港北 2357 | 港南 2358 | 覃塘 2359 | 平南 2360 | 桂平 2361 | 玉林 2362 | 玉林 2363 | 玉州 2364 | 福绵 2365 | 容 2366 | 陆川 2367 | 博白 2368 | 兴业 2369 | 北流 2370 | 百色 2371 | 百色 2372 | 右江 2373 | 田阳 2374 | 田东 2375 | 平果 2376 | 德保 2377 | 那坡 2378 | 凌云 2379 | 乐业 2380 | 田林 2381 | 西林 2382 | 隆林各族 2383 | 靖西 2384 | 贺州 2385 | 贺州 2386 | 八步 2387 | 平桂 2388 | 昭平 2389 | 钟山 2390 | 富川瑶族 2391 | 河池 2392 | 河池 2393 | 金城江 2394 | 宜州 2395 | 南丹 2396 | 天峨 2397 | 凤山 2398 | 东兰 2399 | 罗城仫佬族 2400 | 环江毛南族 2401 | 巴马瑶族 2402 | 都安瑶族 2403 | 大化瑶族 2404 | 来宾 2405 | 来宾 2406 | 兴宾 2407 | 忻城 2408 | 象州 2409 | 武宣 2410 | 金秀瑶族 2411 | 合山 2412 | 崇左 2413 | 崇左 2414 | 江州 2415 | 扶绥 2416 | 宁明 2417 | 龙州 2418 | 大新 2419 | 天等 2420 | 凭祥 2421 | 海南省 2422 | 海口 2423 | 海口 2424 | 秀英 2425 | 龙华 2426 | 琼山 2427 | 美兰 2428 | 三亚 2429 | 三亚 2430 | 海棠 2431 | 吉阳 2432 | 天涯 2433 | 崖州 2434 | 三沙 2435 | 三沙 2436 | 西沙群岛 2437 | 南沙群岛 2438 | 中沙群岛的岛礁及其海域 2439 | 儋州 2440 | 五指山 2441 | 琼海 2442 | 文昌 2443 | 万宁 2444 | 东方 2445 | 定安 2446 | 屯昌 2447 | 澄迈 2448 | 临高 2449 | 白沙黎族 2450 | 昌江黎族 2451 | 乐东黎族 2452 | 陵水黎族 2453 | 保亭黎族苗族 2454 | 琼中黎族苗族 2455 | 重庆 2456 | 重庆 2457 | 万州 2458 | 涪陵 2459 | 渝中 2460 | 大渡口 2461 | 江北 2462 | 沙坪坝 2463 | 九龙坡 2464 | 南岸 2465 | 北碚 2466 | 綦江 2467 | 大足 2468 | 渝北 2469 | 巴南 2470 | 黔江 2471 | 长寿 2472 | 江津 2473 | 合川 2474 | 永川 2475 | 南川 2476 | 璧山 2477 | 铜梁 2478 | 潼南 2479 | 荣昌 2480 | 开州 2481 | 梁平 2482 | 武隆 2483 | 重庆郊 2484 | 城口 2485 | 丰都 2486 | 垫江 2487 | 忠 2488 | 云阳 2489 | 奉节 2490 | 巫山 2491 | 巫溪 2492 | 石柱土家族 2493 | 秀山土家族苗族 2494 | 酉阳土家族苗族 2495 | 彭水苗族土家族 2496 | 四川省 2497 | 成都 2498 | 成都 2499 | 锦江 2500 | 青羊 2501 | 金牛 2502 | 武侯 2503 | 成华 2504 | 龙泉驿 2505 | 青白江 2506 | 新都 2507 | 温江 2508 | 双流 2509 | 郫都 2510 | 金堂 2511 | 大邑 2512 | 蒲江 2513 | 新津 2514 | 都江堰 2515 | 彭州 2516 | 邛崃 2517 | 崇州 2518 | 简阳 2519 | 自贡 2520 | 自贡 2521 | 自流井 2522 | 贡井 2523 | 大安 2524 | 沿滩 2525 | 荣 2526 | 富顺 2527 | 攀枝花 2528 | 攀枝花 2529 | 东 2530 | 西 2531 | 仁和 2532 | 米易 2533 | 盐边 2534 | 泸州 2535 | 泸州 2536 | 江阳 2537 | 纳溪 2538 | 龙马潭 2539 | 泸 2540 | 合江 2541 | 叙永 2542 | 古蔺 2543 | 德阳 2544 | 德阳 2545 | 旌阳 2546 | 中江 2547 | 罗江 2548 | 广汉 2549 | 什邡 2550 | 绵竹 2551 | 绵阳 2552 | 绵阳 2553 | 涪城 2554 | 游仙 2555 | 安州 2556 | 三台 2557 | 盐亭 2558 | 梓潼 2559 | 北川羌族 2560 | 平武 2561 | 江油 2562 | 广元 2563 | 广元 2564 | 利州 2565 | 昭化 2566 | 朝天 2567 | 旺苍 2568 | 青川 2569 | 剑阁 2570 | 苍溪 2571 | 遂宁 2572 | 遂宁 2573 | 船山 2574 | 安居 2575 | 蓬溪 2576 | 射洪 2577 | 大英 2578 | 内江 2579 | 内江 2580 | 中 2581 | 东兴 2582 | 威远 2583 | 资中 2584 | 隆昌 2585 | 乐山 2586 | 乐山 2587 | 中 2588 | 沙湾 2589 | 五通桥 2590 | 金口河 2591 | 犍为 2592 | 井研 2593 | 夹江 2594 | 沐川 2595 | 峨边彝族 2596 | 马边彝族 2597 | 峨眉山 2598 | 南充 2599 | 南充 2600 | 顺庆 2601 | 高坪 2602 | 嘉陵 2603 | 南部 2604 | 营山 2605 | 蓬安 2606 | 仪陇 2607 | 西充 2608 | 阆中 2609 | 眉山 2610 | 眉山 2611 | 东坡 2612 | 彭山 2613 | 仁寿 2614 | 洪雅 2615 | 丹棱 2616 | 青神 2617 | 宜宾 2618 | 宜宾 2619 | 翠屏 2620 | 南溪 2621 | 叙州 2622 | 江安 2623 | 长宁 2624 | 高 2625 | 珙 2626 | 筠连 2627 | 兴文 2628 | 屏山 2629 | 广安 2630 | 广安 2631 | 广安 2632 | 前锋 2633 | 岳池 2634 | 武胜 2635 | 邻水 2636 | 华蓥 2637 | 达州 2638 | 达州 2639 | 通川 2640 | 达川 2641 | 宣汉 2642 | 开江 2643 | 大竹 2644 | 渠 2645 | 万源 2646 | 雅安 2647 | 雅安 2648 | 雨城 2649 | 名山 2650 | 荥经 2651 | 汉源 2652 | 石棉 2653 | 天全 2654 | 芦山 2655 | 宝兴 2656 | 巴中 2657 | 巴中 2658 | 巴州 2659 | 恩阳 2660 | 通江 2661 | 南江 2662 | 平昌 2663 | 资阳 2664 | 资阳 2665 | 雁江 2666 | 安岳 2667 | 乐至 2668 | 阿坝藏族羌族州 2669 | 马尔康 2670 | 汶川 2671 | 理 2672 | 茂 2673 | 松潘 2674 | 九寨沟 2675 | 金川 2676 | 小金 2677 | 黑水 2678 | 壤塘 2679 | 阿坝 2680 | 若尔盖 2681 | 红原 2682 | 甘孜藏族州 2683 | 康定 2684 | 泸定 2685 | 丹巴 2686 | 九龙 2687 | 雅江 2688 | 道孚 2689 | 炉霍 2690 | 甘孜 2691 | 新龙 2692 | 德格 2693 | 白玉 2694 | 石渠 2695 | 色达 2696 | 理塘 2697 | 巴塘 2698 | 乡城 2699 | 稻城 2700 | 得荣 2701 | 凉山彝族州 2702 | 西昌 2703 | 木里藏族 2704 | 盐源 2705 | 德昌 2706 | 会理 2707 | 会东 2708 | 宁南 2709 | 普格 2710 | 布拖 2711 | 金阳 2712 | 昭觉 2713 | 喜德 2714 | 冕宁 2715 | 越西 2716 | 甘洛 2717 | 美姑 2718 | 雷波 2719 | 贵州省 2720 | 贵阳 2721 | 贵阳 2722 | 南明 2723 | 云岩 2724 | 花溪 2725 | 乌当 2726 | 白云 2727 | 观山湖 2728 | 开阳 2729 | 息烽 2730 | 修文 2731 | 清镇 2732 | 六盘水 2733 | 钟山 2734 | 六枝特 2735 | 水城 2736 | 盘州 2737 | 遵义 2738 | 遵义 2739 | 红花岗 2740 | 汇川 2741 | 播州 2742 | 桐梓 2743 | 绥阳 2744 | 正安 2745 | 道真仡佬族苗族 2746 | 务川仡佬族苗族 2747 | 凤冈 2748 | 湄潭 2749 | 余庆 2750 | 习水 2751 | 赤水 2752 | 仁怀 2753 | 安顺 2754 | 安顺 2755 | 西秀 2756 | 平坝 2757 | 普定 2758 | 镇宁布依族苗族 2759 | 关岭布依族苗族 2760 | 紫云苗族布依族 2761 | 毕节 2762 | 七星关 2763 | 大方 2764 | 黔西 2765 | 金沙 2766 | 织金 2767 | 纳雍 2768 | 威宁彝族回族苗族 2769 | 赫章 2770 | 铜仁 2771 | 碧江 2772 | 万山 2773 | 江口 2774 | 玉屏侗族 2775 | 石阡 2776 | 思南 2777 | 印江土家族苗族 2778 | 德江 2779 | 沿河土家族 2780 | 松桃苗族 2781 | 黔西南布依族苗族州 2782 | 兴义 2783 | 兴仁 2784 | 普安 2785 | 晴隆 2786 | 贞丰 2787 | 望谟 2788 | 册亨 2789 | 安龙 2790 | 黔东南苗族侗族州 2791 | 凯里 2792 | 黄平 2793 | 施秉 2794 | 三穗 2795 | 镇远 2796 | 岑巩 2797 | 天柱 2798 | 锦屏 2799 | 剑河 2800 | 台江 2801 | 黎平 2802 | 榕江 2803 | 从江 2804 | 雷山 2805 | 麻江 2806 | 丹寨 2807 | 黔南布依族苗族州 2808 | 都匀 2809 | 福泉 2810 | 荔波 2811 | 贵定 2812 | 瓮安 2813 | 独山 2814 | 平塘 2815 | 罗甸 2816 | 长顺 2817 | 龙里 2818 | 惠水 2819 | 三都水族 2820 | 云南省 2821 | 昆明 2822 | 昆明 2823 | 五华 2824 | 盘龙 2825 | 官渡 2826 | 西山 2827 | 东川 2828 | 呈贡 2829 | 晋宁 2830 | 富民 2831 | 宜良 2832 | 石林彝族 2833 | 嵩明 2834 | 禄劝彝族苗族 2835 | 寻甸回族彝族 2836 | 安宁 2837 | 曲靖 2838 | 曲靖 2839 | 麒麟 2840 | 沾益 2841 | 马龙 2842 | 陆良 2843 | 师宗 2844 | 罗平 2845 | 富源 2846 | 会泽 2847 | 宣威 2848 | 玉溪 2849 | 玉溪 2850 | 红塔 2851 | 江川 2852 | 澄江 2853 | 通海 2854 | 华宁 2855 | 易门 2856 | 峨山彝族 2857 | 新平彝族傣族 2858 | 元江哈尼族彝族傣族 2859 | 保山 2860 | 保山 2861 | 隆阳 2862 | 施甸 2863 | 龙陵 2864 | 昌宁 2865 | 腾冲 2866 | 昭通 2867 | 昭通 2868 | 昭阳 2869 | 鲁甸 2870 | 巧家 2871 | 盐津 2872 | 大关 2873 | 永善 2874 | 绥江 2875 | 镇雄 2876 | 彝良 2877 | 威信 2878 | 水富 2879 | 丽江 2880 | 丽江 2881 | 古城 2882 | 玉龙纳西族 2883 | 永胜 2884 | 华坪 2885 | 宁蒗彝族 2886 | 普洱 2887 | 普洱 2888 | 思茅 2889 | 宁洱哈尼族彝族 2890 | 墨江哈尼族 2891 | 景东彝族 2892 | 景谷傣族彝族 2893 | 镇沅彝族哈尼族拉祜族 2894 | 江城哈尼族彝族 2895 | 孟连傣族拉祜族佤族 2896 | 澜沧拉祜族 2897 | 西盟佤族 2898 | 临沧 2899 | 临沧 2900 | 临翔 2901 | 凤庆 2902 | 云 2903 | 永德 2904 | 镇康 2905 | 双江拉祜族佤族布朗族傣族 2906 | 耿马傣族佤族 2907 | 沧源佤族 2908 | 楚雄彝族州 2909 | 楚雄 2910 | 双柏 2911 | 牟定 2912 | 南华 2913 | 姚安 2914 | 大姚 2915 | 永仁 2916 | 元谋 2917 | 武定 2918 | 禄丰 2919 | 红河哈尼族彝族州 2920 | 个旧 2921 | 开远 2922 | 蒙自 2923 | 弥勒 2924 | 屏边苗族 2925 | 建水 2926 | 石屏 2927 | 泸西 2928 | 元阳 2929 | 红河 2930 | 金平苗族瑶族傣族 2931 | 绿春 2932 | 河口瑶族 2933 | 文山壮族苗族州 2934 | 文山 2935 | 砚山 2936 | 西畴 2937 | 麻栗坡 2938 | 马关 2939 | 丘北 2940 | 广南 2941 | 富宁 2942 | 西双版纳傣族州 2943 | 景洪 2944 | 勐海 2945 | 勐腊 2946 | 大理白族州 2947 | 大理 2948 | 漾濞彝族 2949 | 祥云 2950 | 宾川 2951 | 弥渡 2952 | 南涧彝族 2953 | 巍山彝族回族 2954 | 永平 2955 | 云龙 2956 | 洱源 2957 | 剑川 2958 | 鹤庆 2959 | 德宏傣族景颇族州 2960 | 瑞丽 2961 | 芒 2962 | 梁河 2963 | 盈江 2964 | 陇川 2965 | 怒江傈僳族州 2966 | 泸水 2967 | 福贡 2968 | 贡山独龙族怒族 2969 | 兰坪白族普米族 2970 | 迪庆藏族州 2971 | 香格里拉 2972 | 德钦 2973 | 维西傈僳族 2974 | 西藏 2975 | 拉萨 2976 | 拉萨 2977 | 城关 2978 | 堆龙德庆 2979 | 达孜 2980 | 林周 2981 | 当雄 2982 | 尼木 2983 | 曲水 2984 | 墨竹工卡 2985 | 日喀则 2986 | 桑珠孜 2987 | 南木林 2988 | 江孜 2989 | 定日 2990 | 萨迦 2991 | 拉孜 2992 | 昂仁 2993 | 谢通门 2994 | 白朗 2995 | 仁布 2996 | 康马 2997 | 定结 2998 | 仲巴 2999 | 亚东 3000 | 吉隆 3001 | 聂拉木 3002 | 萨嘎 3003 | 岗巴 3004 | 昌都 3005 | 卡若 3006 | 江达 3007 | 贡觉 3008 | 类乌齐 3009 | 丁青 3010 | 察雅 3011 | 八宿 3012 | 左贡 3013 | 芒康 3014 | 洛隆 3015 | 边坝 3016 | 林芝 3017 | 巴宜 3018 | 工布江达 3019 | 米林 3020 | 墨脱 3021 | 波密 3022 | 察隅 3023 | 朗 3024 | 山南 3025 | 乃东 3026 | 扎囊 3027 | 贡嘎 3028 | 桑日 3029 | 琼结 3030 | 曲松 3031 | 措美 3032 | 洛扎 3033 | 加查 3034 | 隆子 3035 | 错那 3036 | 浪卡子 3037 | 那曲 3038 | 色尼 3039 | 嘉黎 3040 | 比如 3041 | 聂荣 3042 | 安多 3043 | 申扎 3044 | 索 3045 | 班戈 3046 | 巴青 3047 | 尼玛 3048 | 双湖 3049 | 阿里地 3050 | 普兰 3051 | 札达 3052 | 噶尔 3053 | 日土 3054 | 革吉 3055 | 改则 3056 | 措勤 3057 | 陕西省 3058 | 西安 3059 | 西安 3060 | 新城 3061 | 碑林 3062 | 莲湖 3063 | 灞桥 3064 | 未央 3065 | 雁塔 3066 | 阎良 3067 | 临潼 3068 | 长安 3069 | 高陵 3070 | 鄠邑 3071 | 蓝田 3072 | 周至 3073 | 铜川 3074 | 铜川 3075 | 王益 3076 | 印台 3077 | 耀州 3078 | 宜君 3079 | 宝鸡 3080 | 宝鸡 3081 | 渭滨 3082 | 金台 3083 | 陈仓 3084 | 凤翔 3085 | 岐山 3086 | 扶风 3087 | 眉 3088 | 陇 3089 | 千阳 3090 | 麟游 3091 | 凤 3092 | 太白 3093 | 咸阳 3094 | 咸阳 3095 | 秦都 3096 | 杨陵 3097 | 渭城 3098 | 三原 3099 | 泾阳 3100 | 乾 3101 | 礼泉 3102 | 永寿 3103 | 彬州 3104 | 长武 3105 | 旬邑 3106 | 淳化 3107 | 武功 3108 | 兴平 3109 | 渭南 3110 | 渭南 3111 | 临渭 3112 | 华州 3113 | 潼关 3114 | 大荔 3115 | 合阳 3116 | 澄城 3117 | 蒲城 3118 | 白水 3119 | 富平 3120 | 韩城 3121 | 华阴 3122 | 延安 3123 | 延安 3124 | 宝塔 3125 | 安塞 3126 | 延长 3127 | 延川 3128 | 子长 3129 | 志丹 3130 | 吴起 3131 | 甘泉 3132 | 富 3133 | 洛川 3134 | 宜川 3135 | 黄龙 3136 | 黄陵 3137 | 汉中 3138 | 汉中 3139 | 汉台 3140 | 南郑 3141 | 城固 3142 | 洋 3143 | 西乡 3144 | 勉 3145 | 宁强 3146 | 略阳 3147 | 镇巴 3148 | 留坝 3149 | 佛坪 3150 | 榆林 3151 | 榆林 3152 | 榆阳 3153 | 横山 3154 | 府谷 3155 | 靖边 3156 | 定边 3157 | 绥德 3158 | 米脂 3159 | 佳 3160 | 吴堡 3161 | 清涧 3162 | 子洲 3163 | 神木 3164 | 安康 3165 | 安康 3166 | 汉滨 3167 | 汉阴 3168 | 石泉 3169 | 宁陕 3170 | 紫阳 3171 | 岚皋 3172 | 平利 3173 | 镇坪 3174 | 旬阳 3175 | 白河 3176 | 商洛 3177 | 商洛 3178 | 商州 3179 | 洛南 3180 | 丹凤 3181 | 商南 3182 | 山阳 3183 | 镇安 3184 | 柞水 3185 | 甘肃省 3186 | 兰州 3187 | 兰州 3188 | 城关 3189 | 七里河 3190 | 西固 3191 | 安宁 3192 | 红古 3193 | 永登 3194 | 皋兰 3195 | 榆中 3196 | 嘉峪关 3197 | 嘉峪关 3198 | 金昌 3199 | 金昌 3200 | 金川 3201 | 永昌 3202 | 白银 3203 | 白银 3204 | 白银 3205 | 平川 3206 | 靖远 3207 | 会宁 3208 | 景泰 3209 | 天水 3210 | 天水 3211 | 秦州 3212 | 麦积 3213 | 清水 3214 | 秦安 3215 | 甘谷 3216 | 武山 3217 | 张家川回族 3218 | 武威 3219 | 武威 3220 | 凉州 3221 | 民勤 3222 | 古浪 3223 | 天祝藏族 3224 | 张掖 3225 | 张掖 3226 | 甘州 3227 | 肃南裕固族 3228 | 民乐 3229 | 临泽 3230 | 高台 3231 | 山丹 3232 | 平凉 3233 | 平凉 3234 | 崆峒 3235 | 泾川 3236 | 灵台 3237 | 崇信 3238 | 华亭 3239 | 庄浪 3240 | 静宁 3241 | 酒泉 3242 | 酒泉 3243 | 肃州 3244 | 金塔 3245 | 瓜州 3246 | 肃北蒙古族 3247 | 阿克塞哈萨克族 3248 | 玉门 3249 | 敦煌 3250 | 庆阳 3251 | 庆阳 3252 | 西峰 3253 | 庆城 3254 | 环 3255 | 华池 3256 | 合水 3257 | 正宁 3258 | 宁 3259 | 镇原 3260 | 定西 3261 | 定西 3262 | 安定 3263 | 通渭 3264 | 陇西 3265 | 渭源 3266 | 临洮 3267 | 漳 3268 | 岷 3269 | 陇南 3270 | 陇南 3271 | 武都 3272 | 成 3273 | 文 3274 | 宕昌 3275 | 康 3276 | 西和 3277 | 礼 3278 | 徽 3279 | 两当 3280 | 临夏回族州 3281 | 临夏 3282 | 临夏 3283 | 康乐 3284 | 永靖 3285 | 广河 3286 | 和政 3287 | 东乡族 3288 | 积石山保安族东乡族撒拉族 3289 | 甘南藏族州 3290 | 合作 3291 | 临潭 3292 | 卓尼 3293 | 舟曲 3294 | 迭部 3295 | 玛曲 3296 | 碌曲 3297 | 夏河 3298 | 青海省 3299 | 西宁 3300 | 西宁 3301 | 城东 3302 | 城中 3303 | 城西 3304 | 城北 3305 | 大通回族土族 3306 | 湟中 3307 | 湟源 3308 | 海东 3309 | 乐都 3310 | 平安 3311 | 民和回族土族 3312 | 互助土族 3313 | 化隆回族 3314 | 循化撒拉族 3315 | 海北藏族州 3316 | 门源回族 3317 | 祁连 3318 | 海晏 3319 | 刚察 3320 | 黄南藏族州 3321 | 同仁 3322 | 尖扎 3323 | 泽库 3324 | 河南蒙古族 3325 | 海南藏族州 3326 | 共和 3327 | 同德 3328 | 贵德 3329 | 兴海 3330 | 贵南 3331 | 果洛藏族州 3332 | 玛沁 3333 | 班玛 3334 | 甘德 3335 | 达日 3336 | 久治 3337 | 玛多 3338 | 玉树藏族州 3339 | 玉树 3340 | 杂多 3341 | 称多 3342 | 治多 3343 | 囊谦 3344 | 曲麻莱 3345 | 海西蒙古族藏族州 3346 | 格尔木 3347 | 德令哈 3348 | 茫崖 3349 | 乌兰 3350 | 都兰 3351 | 天峻 3352 | 海西蒙古族藏族州直 3353 | 宁夏回族 3354 | 银川 3355 | 银川 3356 | 兴庆 3357 | 西夏 3358 | 金凤 3359 | 永宁 3360 | 贺兰 3361 | 灵武 3362 | 石嘴山 3363 | 石嘴山 3364 | 大武口 3365 | 惠农 3366 | 平罗 3367 | 吴忠 3368 | 吴忠 3369 | 利通 3370 | 红寺堡 3371 | 盐池 3372 | 同心 3373 | 青铜峡 3374 | 固原 3375 | 固原 3376 | 原州 3377 | 西吉 3378 | 隆德 3379 | 泾源 3380 | 彭阳 3381 | 中卫 3382 | 中卫 3383 | 沙坡头 3384 | 中宁 3385 | 海原 3386 | 新疆维吾尔 3387 | 乌鲁木齐 3388 | 乌鲁木齐 3389 | 天山 3390 | 沙依巴克 3391 | 新 3392 | 水磨沟 3393 | 头屯河 3394 | 达坂城 3395 | 米东 3396 | 乌鲁木齐 3397 | 克拉玛依 3398 | 克拉玛依 3399 | 独山子 3400 | 克拉玛依 3401 | 白碱滩 3402 | 乌尔禾 3403 | 吐鲁番 3404 | 高昌 3405 | 鄯善 3406 | 托克逊 3407 | 哈密 3408 | 伊州 3409 | 巴里坤哈萨克 3410 | 伊吾 3411 | 昌吉回族州 3412 | 昌吉 3413 | 阜康 3414 | 呼图壁 3415 | 玛纳斯 3416 | 奇台 3417 | 吉木萨尔 3418 | 木垒哈萨克 3419 | 博尔塔拉蒙古州 3420 | 博乐 3421 | 阿拉山口 3422 | 精河 3423 | 温泉 3424 | 巴音郭楞蒙古州 3425 | 库尔勒 3426 | 轮台 3427 | 尉犁 3428 | 若羌 3429 | 且末 3430 | 焉耆回族 3431 | 和静 3432 | 和硕 3433 | 博湖 3434 | 阿克苏地 3435 | 阿克苏 3436 | 温宿 3437 | 库车 3438 | 沙雅 3439 | 新和 3440 | 拜城 3441 | 乌什 3442 | 阿瓦提 3443 | 柯坪 3444 | 克孜勒苏柯尔克孜州 3445 | 阿图什 3446 | 阿克陶 3447 | 阿合奇 3448 | 乌恰 3449 | 喀什地 3450 | 喀什 3451 | 疏附 3452 | 疏勒 3453 | 英吉沙 3454 | 泽普 3455 | 莎车 3456 | 叶城 3457 | 麦盖提 3458 | 岳普湖 3459 | 伽师 3460 | 巴楚 3461 | 塔什库尔干塔吉克 3462 | 和田地 3463 | 和田 3464 | 和田 3465 | 墨玉 3466 | 皮山 3467 | 洛浦 3468 | 策勒 3469 | 于田 3470 | 民丰 3471 | 伊犁哈萨克州 3472 | 伊宁 3473 | 奎屯 3474 | 霍尔果斯 3475 | 伊宁 3476 | 察布查尔锡伯 3477 | 霍城 3478 | 巩留 3479 | 新源 3480 | 昭苏 3481 | 特克斯 3482 | 尼勒克 3483 | 塔城地 3484 | 塔城 3485 | 乌苏 3486 | 额敏 3487 | 沙湾 3488 | 托里 3489 | 裕民 3490 | 和布克赛尔蒙古 3491 | 阿勒泰地 3492 | 阿勒泰 3493 | 布尔津 3494 | 富蕴 3495 | 福海 3496 | 哈巴河 3497 | 青河 3498 | 吉木乃 3499 | 石河子 3500 | 阿拉尔 3501 | 图木舒克 3502 | 五家渠 3503 | 北屯 3504 | 铁门关 3505 | 双河 3506 | 可克达拉 3507 | 昆玉 3508 | 台湾 3509 | 香港 3510 | 中西 3511 | 湾仔 3512 | 东 3513 | 南 3514 | 油尖旺 3515 | 深水埗 3516 | 九龙城 3517 | 黄大仙 3518 | 观塘 3519 | 荃湾 3520 | 屯门 3521 | 元朗 3522 | 北 3523 | 大埔 3524 | 西贡 3525 | 沙田 3526 | 葵青 3527 | 离岛 3528 | 澳门 3529 | 花地玛堂 3530 | 花王堂 3531 | 望德堂 3532 | 大堂 3533 | 风顺堂 3534 | 嘉模堂 3535 | 路凼填海 3536 | 圣方济各堂 3537 | -------------------------------------------------------------------------------- /plugins/weather/data_source_amap.py: -------------------------------------------------------------------------------- 1 | # weather info source: amap 2 | # tutorial: https://lbs.amap.com/api/webservice/guide/api/weatherinfo/ 3 | # sample json fetch: 4 | # http://restapi.amap.com/v3/weather/weatherInfo?key=AAAAAAAAAAAAA&city=%E6%BE%B3%E9%97%A8&extensions=all 5 | 6 | import json 7 | import re 8 | 9 | import aiohttp 10 | from nonebot import get_bot 11 | 12 | from utils_bot.logging import logger 13 | from utils_bot.typing import Union 14 | 15 | URL_BASE: str = 'http://restapi.amap.com/v3/weather/weatherInfo?key=' 16 | # + 17 | API_KEY: str = get_bot().config.AMAP_WEATHER_API_KEY 18 | # + 19 | URL_MODE: str = '&extensions=all&city=' 20 | # + 21 | # city 22 | 23 | # fetch weather json data 24 | async def fetch(city: str) -> Union[dict, None]: 25 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'} 26 | async with aiohttp.ClientSession() as session: 27 | try: 28 | async with session.get( 29 | f'{URL_BASE}{API_KEY}{URL_MODE}{city}', 30 | headers=headers, timeout=10, ssl=False) as r: 31 | res = json.loads(await r.text(), encoding='utf-8') 32 | assert res['info'].lower() == 'ok' 33 | return res 34 | except Exception as e: 35 | logger.exception(e) 36 | return None 37 | 38 | # yields weather data 39 | def process_weatherdata(resJson: Union[dict, None]) -> str: 40 | if resJson is None: 41 | return '天气服务不可用' 42 | 43 | if not resJson['forecasts']: 44 | return '也许地址有误?' 45 | # location data 46 | province = resJson['forecasts'][0]['province'] 47 | city = resJson['forecasts'][0]['city'] 48 | time = resJson['forecasts'][0]['reporttime'] 49 | 50 | heading = f'预报时间:{time}\n地区:{province} {city}\n' 51 | def process_wind_str(wind: str) -> str: 52 | # possible wind input: '东北', '无风' 53 | return wind + '风' if re.search(r'东|南|西|北', wind) else wind 54 | def resGen(): 55 | try: 56 | for j in range(3): 57 | locCurrDay = resJson['forecasts'][0]['casts'][j] 58 | yield \ 59 | f'''{locCurrDay['date']} 日:{locCurrDay['daytemp']}°C,{locCurrDay['dayweather']},{process_wind_str(locCurrDay['daywind'])} 60 | {locCurrDay['date']} 夜:{locCurrDay['nighttemp']}°C,{locCurrDay['nightweather']},{process_wind_str(locCurrDay['nightwind'])}''' 61 | except (IndexError, KeyError): 62 | pass 63 | return heading + '\n'.join(resGen()) 64 | 65 | async def amap_weather(city: str): 66 | return process_weatherdata(await fetch(city)) 67 | -------------------------------------------------------------------------------- /plugins/weather/data_source_openweather.py: -------------------------------------------------------------------------------- 1 | # weather info source: openweathermap 2 | # tutorial: https://openweathermap.org/forecast5 3 | # sample json fetch: 4 | # http://api.openweathermap.org/data/2.5/forecast?appid=AAAAAAAAAAAAAAA&q=London&units=metric 5 | 6 | import json 7 | import random 8 | 9 | import aiohttp 10 | from nonebot import get_bot 11 | 12 | from utils_bot.datetime import datetime, timedelta 13 | from utils_bot.logging import logger 14 | from utils_bot.typing import Union 15 | 16 | # 'weather' for current weather instead 17 | URL_BASE: str = 'http://api.openweathermap.org/data/2.5/forecast?appid=' 18 | # + 19 | API_KEY: str = get_bot().config.OPENWEATHERMAP_API_KEY 20 | # + 21 | URL_MODE: str = '&units=metric&cnt=25&q=' 22 | # + 23 | # city 24 | 25 | # fetch weather json data 26 | async def fetch(city: str) -> Union[dict, None]: 27 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'} 28 | async with aiohttp.ClientSession() as session: 29 | try: 30 | async with session.get( 31 | f'{URL_BASE}{API_KEY}{URL_MODE}{city}', 32 | headers=headers, timeout=10, ssl=False) as r: 33 | res = json.loads(await r.text()) 34 | status = res['cod'].lower() 35 | assert status == '200' or status == '404' 36 | return res 37 | except Exception as e: 38 | logger.exception(e) 39 | return None 40 | 41 | # yields weather data 42 | def process_weatherdata(resJson: Union[dict, None]) -> str: 43 | if resJson is None: 44 | return 'Temperature data unavailable' 45 | if not 'list' in resJson.keys(): 46 | return '也许地址有误?'+\ 47 | ('','如果这地址是对的,私聊我订正BUG!')[random.choice([0,0,1])] 48 | 49 | # location data 50 | cityChart: dict = resJson['city'] 51 | nation = cityChart['country'] 52 | city = cityChart['name'].upper() 53 | timezone = cityChart['timezone'] 54 | timediff = timedelta(seconds=timezone) 55 | 56 | heading = f'''[ALL TIMES LOCAL] 57 | region: {city}, {nation} 58 | ''' 59 | 60 | def resGen(): 61 | try: 62 | for j in range(0, 25, 4): 63 | directChart: dict = resJson['list'][j] 64 | mainChart = directChart['main'] 65 | weatherChart = directChart['weather'][0] 66 | windChart = directChart['wind'] 67 | # if tempMin and tempMax are same, only display one temp 68 | tempMin = mainChart['temp_min'] 69 | tempMax = mainChart['temp_max'] 70 | temp = f'{tempMin}°C' if tempMin == tempMax \ 71 | else f'{tempMin}-{tempMax}°C' 72 | time = (datetime.utcfromtimestamp(directChart['dt']) + timediff) \ 73 | .strftime('%Y-%m-%d %H:%M:%S') 74 | yield \ 75 | f'''{time}: {weatherChart['main']} ({weatherChart['description']}), {temp} 76 | wind: {windChart['speed']}m/s ({windChart['deg']}°), humidity: {mainChart['humidity']}, pressure: {mainChart['pressure']}hPa''' 77 | except (IndexError, KeyError): 78 | pass 79 | return heading + '\n'.join(resGen()) 80 | 81 | async def openweathermap_weather(city: str): 82 | return process_weatherdata(await fetch(city)) 83 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | APScheduler>=3.6.3 2 | aiosqlite==0.17.0 3 | aiohttp>=3.5.4 4 | aiocqhttp>=1.2.3 5 | beautifulsoup4>=4.8.2 6 | nonebot>=1.5.0 7 | jieba==0.42.1 8 | pytz>=2019.3 9 | translate==3.5.0 10 | 11 | -------------------------------------------------------------------------------- /utils_bot/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cleoold/sendo-erika/61dcc6d7e01a59e3f454a90e3b3094eb571ff312/utils_bot/__init__.py -------------------------------------------------------------------------------- /utils_bot/command_ops.py: -------------------------------------------------------------------------------- 1 | from functools import wraps as _wraps 2 | from time import time as _time 3 | 4 | from nonebot import CommandSession as _CommandSession 5 | from nonebot import on_command as _on_command 6 | from nonebot.command import CommandHandler_T 7 | from nonebot.permission import GROUP_MEMBER, SUPERUSER 8 | 9 | from .logging import logger as _logger 10 | from .typing import Awaitable, Callable, Generator, Iterable 11 | 12 | 13 | # decorator 14 | def force_private(f: Callable[..., Awaitable]) -> Callable[..., Awaitable]: 15 | '''forces a command to be executed only in private chat 16 | :wrapped f's args[0]: must be a CommandSession 17 | ''' 18 | @_wraps(f) 19 | async def wrapped(*args, **kwargs): 20 | session: _CommandSession = args[0] 21 | if session.event.get('group_id') or session.event.get('discuss_id'): 22 | _logger.info('forbidden private command terminated.') 23 | session.finish() 24 | else: 25 | return await f(*args, **kwargs) 26 | return wrapped 27 | 28 | 29 | def global_cooldown(cooldown: int) -> Callable[[Callable[..., Awaitable]], Callable[..., Awaitable]]: 30 | '''limit the rate of a command globally 31 | ''' 32 | def deco(f: Callable[..., Awaitable]) -> Callable[..., Awaitable]: 33 | lastcall = 0.0 34 | @_wraps(f) 35 | async def wrapped(*args, **kwargs): 36 | session: _CommandSession = args[0] 37 | nonlocal lastcall 38 | if _time() - lastcall > cooldown: 39 | await f(*args, **kwargs) 40 | lastcall = _time() 41 | else: 42 | await session.send(f'技能冷却中…… ({cooldown}s)') 43 | return wrapped 44 | return deco 45 | 46 | 47 | ############################################################################## 48 | # these features are subject to low efficiencies, and generate 49 | # commands that might go beyond control. use at own risks 50 | # they are absolutely NOT RECOMMENDED in your implementations 51 | ## command generator 52 | def _names_after_q(names: Iterable[str]) -> Generator[str, None, None]: 53 | for each in names: 54 | yield each 55 | yield f'{each}?' 56 | yield f'{each}?' 57 | 58 | def _names_after_do(names: Iterable[str]) -> Generator[str, None, None]: 59 | for each in names: 60 | yield each 61 | yield f'{each}下' 62 | yield f'{each}一下' 63 | yield f'{each}吧' 64 | 65 | def _names_after_do2(names: Iterable[str]) -> Generator[str, None, None]: 66 | for each in names: 67 | yield each 68 | yield f'给我{each}' 69 | yield f'和我{each}' 70 | yield f'跟我{each}' 71 | 72 | # decorators 73 | def on_grp_command_ask(*names: str) -> Callable[[CommandHandler_T], CommandHandler_T]: 74 | '''default to group chat and superusers. 75 | automatically generates question marks after command names as aliases 76 | ''' 77 | namesNew = _names_after_q(names) 78 | return _on_command(next(namesNew), 79 | aliases=(name for name in namesNew), 80 | permission=SUPERUSER | GROUP_MEMBER) 81 | 82 | def on_grp_command_do(*names: str) -> Callable[[CommandHandler_T], CommandHandler_T]: 83 | '''default to group chat and superusers. 84 | automatically generates some pre/suffixes as aliases 85 | ''' 86 | namesNew = _names_after_do(names) 87 | namesNew = _names_after_do2(namesNew) 88 | namesNew = _names_after_q(namesNew) 89 | return _on_command(next(namesNew), 90 | aliases=(name for name in namesNew), 91 | permission=SUPERUSER | GROUP_MEMBER) 92 | 93 | def on_grp_command(*names: str) -> Callable[[CommandHandler_T], CommandHandler_T]: 94 | '''default to group chat and superusers. 95 | ''' 96 | namesNew = iter(names) 97 | return _on_command(next(namesNew), 98 | aliases=(name for name in namesNew), 99 | permission=SUPERUSER | GROUP_MEMBER) 100 | ############################################################################## 101 | -------------------------------------------------------------------------------- /utils_bot/datetime.py: -------------------------------------------------------------------------------- 1 | from datetime import * 2 | 3 | from pytz import timezone 4 | 5 | # all calls to datetime is associated with this timezone 6 | TZ = timezone('Asia/Shanghai') 7 | -------------------------------------------------------------------------------- /utils_bot/logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | logger = logging.getLogger('sendoerika') 5 | handler = logging.StreamHandler(sys.stdout) 6 | handler.setFormatter( 7 | logging.Formatter('[%(asctime)s %(name)s] %(levelname)s: %(message)s')) 8 | logger.addHandler(handler) 9 | logger.setLevel(logging.INFO) # fixme 10 | -------------------------------------------------------------------------------- /utils_bot/msg_ops.py: -------------------------------------------------------------------------------- 1 | from asyncio import gather as _gather 2 | from nonebot import get_bot as _get_bot 3 | 4 | bot = _get_bot() 5 | SUPERUSERS = bot.config.SUPERUSERS 6 | MY_NAMES = bot.config.NICKNAME.union({'机器人', '机械人', '复读机'}) 7 | 8 | async def send_to_superusers(msg: str): 9 | tasks = [bot.send_private_msg(user_id=eachId, message=msg) for eachId in SUPERUSERS] 10 | await _gather(*tasks) 11 | 12 | def msg_is_calling_me(msg: str) -> bool: 13 | for myName in MY_NAMES: 14 | if myName in msg: 15 | return True 16 | return False 17 | -------------------------------------------------------------------------------- /utils_bot/string_ops.py: -------------------------------------------------------------------------------- 1 | import random as _random 2 | 3 | from .typing import T, Sequence 4 | 5 | # fixes aligning problems for multibyte characters 6 | def my_ljust(s, n, fillchar=' ') -> str: 7 | 'same as ljust' 8 | return s.ljust(n - (len(s.encode("gbk")) - len(s)), fillchar) 9 | 10 | def my_rjust(s, n, fillchar=' ') -> str: 11 | 'same as rjust' 12 | return s.rjust(n - (len(s.encode("gbk")) - len(s)), fillchar) 13 | 14 | def half_none(s: str) -> str: 15 | 'half chance returning s, half chance returning empty string' 16 | return s if _random.choice((0,1,)) else '' 17 | 18 | 19 | def prob_pick(itera: Sequence[T], probs: Sequence[float]) -> T: 20 | ''''picks elements randomly from [iter] according to the cumulative 21 | probability array [probs] which has the same length and ranges from 0 to 1''' 22 | chosen: float = _random.random() 23 | if 0 <= chosen < probs[0]: 24 | return itera[0] 25 | for j in range(len(itera)-1): 26 | if probs[j] <= chosen < probs[j+1]: 27 | return itera[j+1] 28 | return itera[len(itera)-1] 29 | -------------------------------------------------------------------------------- /utils_bot/typing.py: -------------------------------------------------------------------------------- 1 | from typing import * 2 | 3 | T = TypeVar('T') 4 | 5 | def depreciated(f: T) -> T: 6 | return f 7 | --------------------------------------------------------------------------------