mirror of
				https://github.com/maunium/stickerpicker.git
				synced 2025-10-30 00:18:06 +01:00 
			
		
		
		
	Make some basic pack creating endpoints work
This commit is contained in:
		| @@ -13,7 +13,8 @@ | ||||
| # | ||||
| # You should have received a copy of the GNU Affero General Public License | ||||
| # along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| from typing import Dict | ||||
| from typing import Dict, Optional | ||||
| from collections import deque | ||||
| import json | ||||
|  | ||||
| from aiohttp import web | ||||
| @@ -99,6 +100,14 @@ class _ErrorMeta: | ||||
|         return web.HTTPNotFound(**self._make_error("NET.MAUNIUM_PACK_NOT_FOUND", | ||||
|                                                    "Sticker pack not found")) | ||||
|  | ||||
|     def schema_error(self, message: str, path: Optional[deque] = None) -> web.HTTPException: | ||||
|         if path: | ||||
|             path_str = "in " + " → ".join(str(part) for part in path) | ||||
|         else: | ||||
|             path_str = "at top level" | ||||
|         return web.HTTPBadRequest(**self._make_error( | ||||
|             "M_BAD_REQUEST", f"Schema validation error {path_str}: {message}")) | ||||
|  | ||||
|     @property | ||||
|     def client_well_known_error(self) -> web.HTTPException: | ||||
|         return web.HTTPForbidden(**self._make_error("NET.MAUNIUM_CLIENT_WELL_KNOWN_ERROR", | ||||
|   | ||||
							
								
								
									
										126
									
								
								sticker/server/api/pack.schema.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								sticker/server/api/pack.schema.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| { | ||||
|   "$schema": "http://json-schema.org/draft-07/schema#", | ||||
|   "type": "object", | ||||
|   "description": "A sticker pack compatible with maunium-stickerpicker", | ||||
|   "properties": { | ||||
|     "id": { | ||||
|       "type": "string", | ||||
|       "description": "An unique identifier for the sticker pack", | ||||
|       "readOnly": true | ||||
|     }, | ||||
|     "title": { | ||||
|       "type": "string", | ||||
|       "description": "The title of the sticker pack" | ||||
|     }, | ||||
|     "stickers": { | ||||
|       "type": "array", | ||||
|       "description": "The stickers in the pack", | ||||
|       "items": { | ||||
|         "type": "object", | ||||
|         "description": "A single sticker", | ||||
|         "properties": { | ||||
|           "id": { | ||||
|             "type": "string", | ||||
|             "description": "An unique identifier for the sticker" | ||||
|           }, | ||||
|           "url": { | ||||
|             "type": "string", | ||||
|             "description": "The Matrix content URI to the sticker", | ||||
|             "pattern": "mxc://.+?/.+" | ||||
|           }, | ||||
|           "body": { | ||||
|             "type": "string", | ||||
|             "description": "The description text for the sticker" | ||||
|           }, | ||||
|           "info": { | ||||
|             "type": "object", | ||||
|             "description": "Matrix media info", | ||||
|             "properties": { | ||||
|               "w": { | ||||
|                 "type": "integer", | ||||
|                 "description": "The intended display width of the sticker" | ||||
|               }, | ||||
|               "h": { | ||||
|                 "type": "integer", | ||||
|                 "description": "The intended display height of the sticker" | ||||
|               }, | ||||
|               "size": { | ||||
|                 "type": "integer", | ||||
|                 "description": "The size of the sticker image in bytes" | ||||
|               }, | ||||
|               "mimetype": { | ||||
|                 "type": "string", | ||||
|                 "description": "The mime type of the sticker image" | ||||
|               } | ||||
|             }, | ||||
|             "additionalProperties": true, | ||||
|             "required": [ | ||||
|               "w", | ||||
|               "h", | ||||
|               "size", | ||||
|               "mimetype" | ||||
|             ] | ||||
|           }, | ||||
|           "net.maunium.telegram.sticker": { | ||||
|             "type": "object", | ||||
|             "description": "Telegram metadata about the sticker", | ||||
|             "properties": { | ||||
|               "pack": { | ||||
|                 "type": "string", | ||||
|                 "description": "Information about the pack the sticker is in", | ||||
|                 "properties": { | ||||
|                   "id": { | ||||
|                     "type": "string", | ||||
|                     "description": "The ID of the sticker pack" | ||||
|                   }, | ||||
|                   "short_name": { | ||||
|                     "type": "string", | ||||
|                     "description": "The short name of the Telegram sticker pack from t.me/addstickers/<shortname>" | ||||
|                   } | ||||
|                 } | ||||
|               }, | ||||
|               "id": { | ||||
|                 "type": "string", | ||||
|                 "description": "The ID of the sticker document" | ||||
|               }, | ||||
|               "emoticons": { | ||||
|                 "type": "array", | ||||
|                 "description": "Emojis that are associated with the sticker", | ||||
|                 "items": { | ||||
|                   "type": "string", | ||||
|                   "description": "A single unicode emoji" | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "required": [ | ||||
|           "id", | ||||
|           "url", | ||||
|           "body", | ||||
|           "info" | ||||
|         ], | ||||
|         "additionalProperties": true | ||||
|       } | ||||
|     }, | ||||
|     "net.maunium.telegram.pack": { | ||||
|       "type": "object", | ||||
|       "description": "Telegram metadata about the pack", | ||||
|       "properties": { | ||||
|         "short_name": { | ||||
|           "type": "string", | ||||
|           "description": "The short name of the Telegram sticker pack from t.me/addstickers/<shortname>" | ||||
|         }, | ||||
|         "hash": { | ||||
|           "type": "string", | ||||
|           "description": "The Telegram-specified hash of the stickerpack that can be used to quickly check if it has changed" | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "additionalProperties": true, | ||||
|   "required": [ | ||||
|     "title", | ||||
|     "stickers" | ||||
|   ] | ||||
| } | ||||
| @@ -13,12 +13,22 @@ | ||||
| # | ||||
| # You should have received a copy of the GNU Affero General Public License | ||||
| # along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| from aiohttp import web | ||||
| from typing import Any | ||||
| import random | ||||
| import string | ||||
| import json | ||||
|  | ||||
| from ..database import User, AccessToken | ||||
| from aiohttp import web | ||||
| from pkg_resources import resource_stream | ||||
| import jsonschema | ||||
|  | ||||
| from ..database import User, AccessToken, Pack, Sticker | ||||
| from .errors import Error | ||||
|  | ||||
| routes = web.RouteTableDef() | ||||
|  | ||||
| pack_schema = json.load(resource_stream("sticker.server.api", "pack.schema.json")) | ||||
|  | ||||
|  | ||||
| @routes.get("/whoami") | ||||
| async def whoami(req: web.Request) -> web.Response: | ||||
| @@ -30,3 +40,68 @@ async def whoami(req: web.Request) -> web.Response: | ||||
|         "homeserver_url": user.homeserver_url, | ||||
|         "last_seen": int(token.last_seen_date.timestamp() / 60) * 60, | ||||
|     }) | ||||
|  | ||||
|  | ||||
| @routes.get("/packs") | ||||
| async def packs(req: web.Request) -> web.Response: | ||||
|     user: User = req["user"] | ||||
|     packs = await user.get_packs() | ||||
|     return web.json_response([pack.to_dict() for pack in packs]) | ||||
|  | ||||
|  | ||||
| async def get_json(req: web.Request, schema: str) -> Any: | ||||
|     try: | ||||
|         data = await req.json() | ||||
|     except json.JSONDecodeError: | ||||
|         raise Error.request_not_json | ||||
|     try: | ||||
|         jsonschema.validate(data, schema) | ||||
|     except jsonschema.ValidationError as e: | ||||
|         raise Error.schema_error(e.message, e.path) | ||||
|     return data | ||||
|  | ||||
|  | ||||
| @routes.post("/packs/create") | ||||
| async def upload_pack(req: web.Request) -> web.Response: | ||||
|     data = await get_json(req, pack_schema) | ||||
|     user: User = req["user"] | ||||
|     title = data.pop("title") | ||||
|     raw_stickers = data.pop("stickers") | ||||
|     pack_id_suffix = data.pop("id", "".join(random.choices(string.ascii_lowercase, k=12))) | ||||
|     pack = Pack(id=f"{user.id}_{pack_id_suffix}", owner=user.id, title=title, meta=data) | ||||
|     stickers = [Sticker(pack_id=pack.id, id=sticker.pop("id"), url=sticker.pop("url"), | ||||
|                         body=sticker.pop("body"), meta=sticker) for sticker in raw_stickers] | ||||
|     await pack.insert() | ||||
|     await pack.set_stickers(stickers) | ||||
|     await user.add_pack(pack) | ||||
|  | ||||
|     return web.json_response({ | ||||
|         **pack.to_dict(), | ||||
|         "stickers": [sticker.to_dict() for sticker in stickers], | ||||
|     }) | ||||
|  | ||||
|  | ||||
| @routes.get("/pack/{pack_id}") | ||||
| async def get_pack(req: web.Request) -> web.Response: | ||||
|     user: User = req["user"] | ||||
|     pack = await user.get_pack(req.match_info["pack_id"]) | ||||
|     if pack is None: | ||||
|         raise Error.pack_not_found | ||||
|     return web.json_response({ | ||||
|         **pack.to_dict(), | ||||
|         "stickers": [sticker.to_dict() for sticker in await pack.get_stickers()], | ||||
|     }) | ||||
|  | ||||
|  | ||||
| @routes.delete("/pack/{pack_id}") | ||||
| async def delete_pack(req: web.Request) -> web.Response: | ||||
|     user: User = req["user"] | ||||
|     pack = await user.get_pack(req.match_info["pack_id"]) | ||||
|     if pack is None: | ||||
|         raise Error.pack_not_found | ||||
|  | ||||
|     if pack.owner != user.id: | ||||
|         await user.remove_pack(pack) | ||||
|     else: | ||||
|         await pack.delete() | ||||
|     return web.Response(status=204) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user