class Web(BaseComponent, LoggerMixin): """ Web Extension """ name = "Web" setting_prefix = "WEB_" def __init__(self, service, name: str = None, setting_prefix: str = None): """ :param service: :type service: :param name: :type name: str :param setting_prefix: :type setting_prefix: str """ super(Web, self).__init__(service, name, setting_prefix) self.app = Sanic(self.name) self.app.config["SERVICE"] = self.service # configure normal route self.app.add_route(self.home, "/") self.server = None # type: ignore async def start(self) -> None: """ start this extension :return: :rtype: None """ ssl_context: Optional[ssl.SSLContext] if self.config["SSL_CERT_FILE"]: ssl_context = ssl.create_default_context( purpose=ssl.Purpose.CLIENT_AUTH) ssl_context.load_cert_chain( certfile=self.config["SSL_CERT_FILE"], keyfile=self.config["SSL_KEY_FILE"], password=self.config["SSL_PASSWORD"], ) else: ssl_context = None self.server = await self.app.create_server( host=self.config["ADDRESS"], port=self.config["PORT"], debug=self.config["DEBUG"], ssl=ssl_context, return_asyncio_server=True, ) self.logger.info("Extension [%s] is running...", self.name) async def stop(self) -> None: """ stop this extension :return: :rtype: None """ if self.server: self.server.close() self.logger.info("Extension [%s] is stopped.", self.name) async def home( # pylint: disable=unused-argument self, request: Request) -> HTTPResponse: """ :param request: :type request: Request :return: :rtype: HTTPResponse """ return json({"hello": "world"})
class TestSanic(unittest.TestCase): app = None def setUp(self): modules.flags_manager.remove_all_flags() self.app = Sanic() self.app.add_route(test_api, '/test', methods=['POST']) init_sanic('test_app', self.app, setup_kuber_config_loader=False) flags.DEFINE_INTEGER_FLAG("test") self.loader = KuberConfigLoader("test_service") self.loader.load_config( config_pb2.GlobalConfig( flags=[{ "name": "test", "type": "INTEGER", "value": { "base_value": { "number_value": 1 } } }], experiments={ 2: ExperimentDefinition( id=2, flag_values={ "test": FlagValue(base_value={"number_value": 2}) }) })) self.server = Process(target=self.app.run, args=("127.0.0.1", 8008)) self.server.start() sleep(1) def tearDown(self): self.server.terminate() def test_experiments(self): headers = { 'x-internal-state-bin': 'EgA=' # experiments: [] } data = requests.post(url='http://127.0.0.1:8008/test', headers=headers).json() self.assertEqual(data['test'], 1) headers = { 'x-internal-state-bin': 'EgEC' # experiments: [2] } data = requests.post(url='http://127.0.0.1:8008/test', headers=headers).json() self.assertEqual(data['test'], 2) def test_trace_info(self): headers = { 'x-internal-trace-info-bin': 'ChYSCzA5MTIzNDU2Nzg5CgcxLjIuMy40' # ip: 1.2.3.4, phone: 09123456789 } data = requests.post(url='http://127.0.0.1:8008/test', headers=headers).json() self.assertEqual(data['trace_info']['client']['ip'], '1.2.3.4') self.assertEqual(data['trace_info']['client']['phone'], '09123456789')
class Hiyobot: commands: dict[str, RegisterdInfo] = {} compoents: dict[str, Component] = {} def __init__( self, client_public_key: str, token: str, uri: str = "discord_interaction", production: bool = False, ) -> None: self.sanic = Sanic("hiyobot") self.uri = uri self.token = token self.client_public_key = client_public_key self.__setup() def __setup(self): self.sanic.add_route(self.__handler, self.uri, ["POST"]) # Injection self.sanic.ctx.http = DiscordBaseHTTP(self.token) self.sanic.ctx.mintchoco = Mintchoco() self.sanic.ctx.handler = self self.sanic.ctx.request = BaseHTTP() def command_register(self, info: RegisterCommand): self.commands.update({info.registerd_info.name: info.registerd_info}) def component_register(self, component: Component): for k in component.mapping.keys(): self.compoents[k] = component if not component.start_check_timeout: create_task(component.timeout_manager(self)) @staticmethod def verify_signiture( raw_body: bytes, signature: str, timestamp: str, client_public_key: str ): message = timestamp.encode() + raw_body try: vk = VerifyKey(bytes.fromhex(client_public_key)) vk.verify(message, bytes.fromhex(signature)) # type: ignore return True except BadSignatureError: return False async def __handler(self, request: HiyobotRequest): request.ctx.response = Response(request) request.ctx.interaction = Interaction(request.json) signature = request.headers.get("X-Signature-Ed25519") timestamp = request.headers.get("X-Signature-Timestamp") if ( signature and timestamp and self.verify_signiture( request.body, signature, timestamp, self.client_public_key ) ): if request.json["type"] == 1: return json({"type": 1}) if request.json["type"] == 2: return await self.dispatch_application_command(request) if request.json["type"] == 3: return await self.dispatch_message_component(request) raise Unauthorized("not_verified", 401) async def dispatch_application_command(self, request: HiyobotRequest): if request.ctx.interaction.data: interaction_data = cast( ApplicationCommandInteractionData, request.ctx.interaction.data ) if interaction_data.get("type") == 1: if interaction_data["name"] in self.commands: command = self.commands[interaction_data["name"]] if interaction_options := interaction_data.get("options"): option = interaction_options[0] # Handle Subcommand if option["type"] == 1: if option["name"] in command.sub_command: sub_command_func = command.sub_command[option["name"]] await sub_command_func( request, *tuple(map(lambda x: x["value"], option["options"])), # type: ignore ) # Handle Single Command else: if interaction_data["name"] in command.single_command: single_command_func = command.single_command[ interaction_data["name"] ] await single_command_func( request, *tuple( map(lambda x: x["value"], option["options"]) # type: ignore ), )