text: # -*- coding: utf-8 -*-
  import asyncio
  import json
  import logging
  import sys
  from string import Template
  
  from pastepwn.util import Request, DictWrapper
  from .basicaction import BasicAction
  
  
  class DiscordAction(BasicAction):
      """Action to send a Discord message to a certain webhook or channel."""
      name = "DiscordAction"
  
      def __init__(self, webhook=None, token=None, channel_id=None, template=None):
          super().__init__()
          self.logger = logging.getLogger(__name__)
          self.bot_available = True
  
          try:
              import websockets
          except ImportError:
              self.logger.warning("Could not import 'websockets' module. So you can only use webhooks for discord.")
              self.bot_available = False
  
          self.webhook = webhook
          if webhook is None:
              if token is None or channel_id is None:
                  raise ValueError('Invalid arguments: requires either webhook or token+channel_id arguments')
  
              if not self.bot_available:
                  raise NotImplementedError("You can't use bot functionality without the 'websockets' module. Please import it or use webhooks!")
  
              self.token = token
              self.channel_id = channel_id
              self.identified = False
  
          if template is not None:
              self.template = Template(template)
          else:
              self.template = None
  
      @asyncio.coroutine
      def _identify(self, ws_url):
          """Connect to the Discord Gateway to identify the bot."""
          # Docs: https://discordapp.com/developers/docs/topics/gateway#connecting-to-the-gateway
          # Open connection to the Discord Gateway
          socket = yield from websockets.connect(ws_url + '/?v=6&encoding=json')
          try:
              # Receive Hello
              hello_str = yield from socket.recv()
              hello = json.loads(hello_str)
              if hello.get('op') != 10:
                  self.logger.warning('[ws] Expected Hello payload but received %s', hello_str)
              
              # Send heartbeat and receive ACK
              yield from socket.send(json.dumps({"op": 1, "d": {}}))
              ack_str = yield from socket.recv()
              ack = json.loads(ack_str)
              if ack.get('op') != 11:
                  self.logger.warning('[ws] Expected Heartbeat ACK payload but received %s', ack_str)
  
              # Identify
              payload = {
                  "token": self.token,
                  "properties": {
                      "$os": sys.platform,
                      "$browser": "pastepwn",
                      "$device": "pastepwn"
                  }
              }
              yield from socket.send(json.dumps({"op": 2, "d": payload}))
  
              # Receive READY event
              ready_str = yield from socket.recv()
              ready = json.loads(ready_str)
              if ready.get('t') != 'READY':
                  self.logger.warning('[ws] Expected READY event but received %s', ready_str)
          finally:
              # Close websocket connection
              yield from socket.close()
  
      def initialize_gateway(self):
          """Initialize the bot token so Discord identifies it properly."""
          if self.webhook is not None:
              raise NotImplementedError('Gateway initialization is only necessary for bot accounts.')
  
          # Call Get Gateway Bot to get the websocket URL
          r = Request()
          r.headers = {'Authorization': 'Bot {}'.format(self.token)}
          res = json.loads(r.get('https://discordapp.com/api/gateway/bot'))
          ws_url = res.get('url')
  
          # Start websocket client
          loop = asyncio.new_event_loop()
          asyncio.set_event_loop(loop)
          loop.run_until_complete(self._identify(ws_url))
          self.identified = True
  
      def perform(self, paste, analyzer_name=None):
          """Send a message via Discord to a specified channel, without checking for errors"""
          r = Request()
          if self.template is None:
              text = "New paste matched by analyzer '{0}' - Link: {1}".format(analyzer_name, paste.full_url)
          else:
              paste_dict = paste.to_dict()
              paste_dict["analyzer_name"] = analyzer_name
              text = self.template.safe_substitute(DictWrapper(paste_dict))
  
          if self.webhook is not None:
              # Send to a webhook (no authentication)
              url = self.webhook
          else:
              # Send through Discord bot API (header-based authentication)
              url = 'https://discordapp.com/api/channels/{0}/messages'.format(self.channel_id)
              r.headers = {'Authorization': 'Bot {}'.format(self.token)}
  
          res = r.post(url, {"content": text})
          if res == "":
              # If the response is empty, skip further execution
              return
  
          res = json.loads(res)
  
          if res.get('code') == 40001 and self.bot_available and self.webhook is None and not self.identified:
              # Unauthorized access, bot token hasn't been identified to Discord Gateway
              self.logger.info('Accessing Discord Gateway to initialize token')
              self.initialize_gateway()
              # Retry action
              self.perform(paste, analyzer_name=analyzer_name)
  