NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

goalMessenger.py (18289B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\corporation\client\goals\goalMessenger.py
      2 import datetime
      3 import corporation.client.goals.goalMessengerSignals as messenger_signals
      4 import datetimeutils
      5 import gametime
      6 from six.moves import http_client as httplib
      7 import logging
      8 import uuid
      9 from corporation.client.goals.errors import GoalMessengerError, AtGoalCapacity, GoalError, GoalNotFound, BadRequestToReserveAsset, WalletAccessForbidden, InternalErrorReservingAsset, InvalidExpirationTime
     10 from corporation.client.goals.goalMessengerUtil import goal_formatter, add_contribution_method
     11 from dateutil.relativedelta import relativedelta
     12 from eveProto.generated.eve_public.corporationgoal.api.notices_pb2 import RedeemedNotice, ExpiredNotice
     13 from eveProto.generated.eve_public.corporationgoal.api.requests_pb2 import GetActiveRequest, GetActiveResponse, GetMyContributorSummaryForGoalRequest, GetMyContributorSummaryForGoalResponse, GetInactiveRequest, GetInactiveResponse, GetContributorSummariesForGoalRequest, GetContributorSummariesForGoalResponse, RedeemMyRewardsRequest, RedeemMyRewardsResponse, GetMineWithRewardsRequest, GetMineWithRewardsResponse
     14 from eveProto.generated.eve_public.goal.api.notices_pb2 import ClosedNotice, CompletedNotice, CreatedNotice, DeletedNotice, ProgressedNotice, NameChangedNotice, DescriptionChangedNotice
     15 from eveProto.generated.eve_public.goal.api.requests_pb2 import CreateRequest, CreateResponse, GetRequest, GetResponse, CloseRequest, CloseResponse, DeleteRequest, DeleteResponse, SetCurrentProgressRequest, SetCurrentProgressResponse, GetCapacityResponse, GetCapacityRequest
     16 from eveProto.generated.eve_public.goal.contribution.api.notices_pb2 import ContributedNotice
     17 from eveProto.generated.eve_public.goal.goal_pb2 import RewardPool, PaymentPeriod
     18 from eveProto.monolith_converters.isk import split_isk
     19 from goals.common.goalConst import CareerPathId, UPDATE_TIMEOUT_SECONDS
     20 from stackless_response_router.exceptions import TimeoutException
     21 logger = logging.getLogger('corporation_goals')
     22 CAREER_PATH_RESOLVER = {CareerPathId.UNSPECIFIED: CreateRequest.Career.CAREER_UNSPECIFIED,
     23  CareerPathId.EXPLORER: CreateRequest.Career.CAREER_EXPLORER,
     24  CareerPathId.INDUSTRIALIST: CreateRequest.Career.CAREER_INDUSTRIALIST,
     25  CareerPathId.SOLDIER_OF_FORTUNE: CreateRequest.Career.CAREER_SOLDIER_OF_FORTUNE,
     26  CareerPathId.ENFORCER: CreateRequest.Career.CAREER_ENFORCER}
     27 PAGE_SIZE = 25
     28 MIN_PROJECT_TIME = 15
     29 MAX_PROJECT_TIME = 1
     30 
     31 class GoalMessenger(object):
     32     _instance = None
     33     public_gateway = None
     34 
     35     @classmethod
     36     def get_instance(cls, public_gateway):
     37         if not cls._instance:
     38             cls._instance = GoalMessenger(public_gateway)
     39         return cls._instance
     40 
     41     def __init__(self, public_gateway):
     42         self.public_gateway = public_gateway
     43         self._subscribe_to_notices()
     44 
     45     def _subscribe_to_notices(self):
     46         self.public_gateway.subscribe_to_notice(RedeemedNotice, lambda payload, _: self.on_redeemed(goal_id=uuid.UUID(bytes=payload.goal.uuid), quantity=payload.quantity))
     47         self.public_gateway.subscribe_to_notice(ClosedNotice, lambda payload, _: self.on_goal_closed(goal_id=uuid.UUID(bytes=payload.goal.uuid)))
     48         self.public_gateway.subscribe_to_notice(CompletedNotice, lambda payload, _d: self.on_goal_completed(goal_id=uuid.UUID(bytes=payload.goal.uuid)))
     49         self.public_gateway.subscribe_to_notice(CreatedNotice, lambda payload, _: self.on_goal_created(goal_id=uuid.UUID(bytes=payload.id.uuid), goal_data=payload.goal))
     50         self.public_gateway.subscribe_to_notice(DeletedNotice, lambda payload, _: self.on_goal_deleted(goal_id=uuid.UUID(bytes=payload.goal.uuid)))
     51         self.public_gateway.subscribe_to_notice(ContributedNotice, lambda payload, _: self.on_goal_contribution(goal_id=uuid.UUID(bytes=payload.goal.uuid), previous_progress=payload.previous_progress, current_progress=payload.current_progress))
     52         self.public_gateway.subscribe_to_notice(ProgressedNotice, lambda payload, _: self.on_goal_progressed(goal_id=uuid.UUID(bytes=payload.goal.uuid), current_progress=payload.current_progress))
     53         self.public_gateway.subscribe_to_notice(NameChangedNotice, lambda payload, _: self.on_name_changed(goal_id=uuid.UUID(bytes=payload.goal.uuid), new_name=payload.admin_designated_new_name))
     54         self.public_gateway.subscribe_to_notice(DescriptionChangedNotice, lambda payload, _: self.on_description_changed(goal_id=uuid.UUID(bytes=payload.goal.uuid), new_description=payload.admin_designated_new_description))
     55         self.public_gateway.subscribe_to_notice(ExpiredNotice, lambda payload, _: self.on_goal_expired(goal_id=uuid.UUID(bytes=payload.goal.uuid)))
     56 
     57     def get_goal(self, goal_id):
     58         request = GetRequest()
     59         request.goal.uuid = goal_id.bytes
     60         logger.info('GetRequest - sending for goal %s', goal_id)
     61         payload = self._send_request(request, GetResponse)
     62         formatted_goal = goal_formatter(goal_id=goal_id, goal=payload.goal)
     63         logger.info('GetRequest - received data for goal %s: %s', goal_id, formatted_goal)
     64         return formatted_goal
     65 
     66     def get_active_goal_ids(self, token = '', size = PAGE_SIZE):
     67         request = GetActiveRequest()
     68         request.page.size = size
     69         request.page.token = token
     70         payload = self._send_request(request, GetActiveResponse)
     71         goal_ids = [ uuid.UUID(bytes=goal_id.uuid) for goal_id in payload.goal_ids ]
     72         token = payload.next_page.token
     73         return (token, goal_ids)
     74 
     75     def get_unclaimed_goal_ids(self, token = '', size = PAGE_SIZE):
     76         request = GetMineWithRewardsRequest()
     77         request.page.size = size
     78         request.page.token = token
     79         payload = self._send_request(request, GetMineWithRewardsResponse)
     80         goal_ids = [ uuid.UUID(bytes=goal_id.uuid) for goal_id in payload.identifiers ]
     81         token = payload.next_page.token
     82         return (token, goal_ids)
     83 
     84     def get_inactive_goal_ids(self, start_time, duration, token = '', size = PAGE_SIZE):
     85         request = GetInactiveRequest()
     86         request.ended_timespan.start_time.FromSeconds(start_time)
     87         request.ended_timespan.duration.FromSeconds(duration)
     88         request.page.size = size
     89         request.page.token = token
     90         payload = self._send_request(request, GetInactiveResponse)
     91         goal_ids = [ uuid.UUID(bytes=goal_id.uuid) for goal_id in payload.goal_ids ]
     92         token = payload.next_page.token
     93         return (token, goal_ids)
     94 
     95     def get_my_contributor_summary(self, goal_id):
     96         request = GetMyContributorSummaryForGoalRequest()
     97         request.goal.uuid = goal_id.bytes
     98         payload = self._send_request(request, GetMyContributorSummaryForGoalResponse, not_found_as_none=True)
     99         if payload is None:
    100             progress = 0
    101             unclaimed = 0
    102         else:
    103             progress = payload.summary.progress
    104             if payload.summary.earnings:
    105                 unclaimed = payload.summary.earnings[0].quantity.total
    106             else:
    107                 unclaimed = 0
    108         return {'progress': progress,
    109          'rewards_unclaimed': unclaimed}
    110 
    111     def get_all_contributor_summaries_for_goal(self, goal_id, token = '', size = PAGE_SIZE):
    112         request = GetContributorSummariesForGoalRequest()
    113         request.goal.uuid = goal_id.bytes
    114         request.page.size = size
    115         request.page.token = token
    116         payload = self._send_request(request, GetContributorSummariesForGoalResponse)
    117         summaries = []
    118         for summary in payload.summaries:
    119             summaries.append((summary.contributor.sequential, summary.progress))
    120 
    121         next_token = payload.next_page.token
    122         return (next_token, summaries)
    123 
    124     def get_capacity(self):
    125         request = GetCapacityRequest()
    126         payload = self._send_request(request, GetCapacityResponse)
    127         return payload.capacity
    128 
    129     def redeem_reward(self, goal_id):
    130         request = RedeemMyRewardsRequest()
    131         request.goal_id.uuid = goal_id.bytes
    132         self._send_request(request, RedeemMyRewardsResponse)
    133 
    134     def on_redeemed(self, goal_id, quantity):
    135         logger.info('Emitting Goal Redeemed Notice for goal %s and quantity %s' % (goal_id, quantity))
    136         messenger_signals.on_goal_redeemed_internal(goal_id, quantity)
    137 
    138     def on_goal_created(self, goal_id, goal_data):
    139         logger.info('Emitting Goal Created Notice %s' % goal_id)
    140         format_goal_data = goal_formatter(goal_id, goal_data)
    141         messenger_signals.on_goal_created_internal(goal_id, format_goal_data)
    142 
    143     def on_goal_deleted(self, goal_id):
    144         logger.info('Emitting Goal Deleted Notice %s' % goal_id)
    145         messenger_signals.on_goal_deleted_internal(goal_id)
    146 
    147     def on_goal_closed(self, goal_id):
    148         logger.info('Emitting Goal Closed Notice %s' % goal_id)
    149         messenger_signals.on_goal_closed_internal(goal_id)
    150 
    151     def on_goal_completed(self, goal_id):
    152         logger.info('Emitting Goal Completed Notice %s' % goal_id)
    153         messenger_signals.on_goal_completed_internal(goal_id)
    154 
    155     def on_goal_contribution(self, goal_id, previous_progress, current_progress):
    156         progress_added = current_progress - previous_progress
    157         logger.info('Emitting OnGoalProgressAdded Notice %s, incrementing by %s, setting progress to %s', goal_id, progress_added, current_progress)
    158         messenger_signals.on_goal_contribution_internal(goal_id, progress_added, current_progress)
    159 
    160     def on_goal_progressed(self, goal_id, current_progress):
    161         logger.info('Emitting OnGoalProgressSet Notice %s, progress set to %s', goal_id, current_progress)
    162         messenger_signals.on_goal_progress_set_internal(goal_id, current_progress)
    163 
    164     def on_name_changed(self, goal_id, new_name):
    165         logger.info('Emitting OnGoalNameChanged Notice %s, name set to %s', goal_id, new_name)
    166         messenger_signals.on_goal_name_changed_internal(goal_id, new_name)
    167 
    168     def on_description_changed(self, goal_id, new_description):
    169         logger.info('Emitting OnGoalDescriptionChanged Notice %s, name set to %s', goal_id, new_description)
    170         messenger_signals.on_goal_description_changed_internal(goal_id, new_description)
    171 
    172     def on_goal_expired(self, goal_id):
    173         logger.info('Emitting Goal Expired Notice %s' % goal_id)
    174         messenger_signals.on_goal_expired_internal(goal_id)
    175 
    176     def create_goal(self, name, description, desired_progress, method_id, contribution_fields, career_path, payment = None, expiration = None, validate_end_date = True, participation_limit = None, coverage_limit = None, multiplier = None):
    177         request = CreateRequest()
    178         request.name = name
    179         request.description = description
    180         request.desired_progress = desired_progress
    181         add_contribution_method(request, method_id, contribution_fields)
    182         if career_path in CAREER_PATH_RESOLVER:
    183             request.career = request.career = CAREER_PATH_RESOLVER[career_path]
    184         else:
    185             raise ValueError('Career path not supported')
    186         if payment:
    187             units, nanos = split_isk(payment)
    188             reward = RewardPool()
    189             reward.period = PaymentPeriod.PAYMENT_PERIOD_CONTRIBUTION
    190             reward.isk.amount.units = units
    191             reward.isk.amount.nanos = nanos
    192             request.reward_pools.append(reward)
    193         if expiration:
    194             expiration_datetime = datetimeutils.filetime_to_datetime(expiration)
    195             if validate_end_date:
    196                 now = gametime.now()
    197                 min_datetime = now + relativedelta(minutes=MIN_PROJECT_TIME)
    198                 max_datetime = datetime.datetime.combine(now + relativedelta(years=MAX_PROJECT_TIME), datetime.time.max)
    199                 if max_datetime < expiration_datetime or expiration_datetime < min_datetime:
    200                     raise InvalidExpirationTime('Expiration time too short or long')
    201             request.timestamp.FromDatetime(expiration_datetime)
    202         else:
    203             request.no_expiry = True
    204         if participation_limit:
    205             request.limit = participation_limit
    206         else:
    207             request.unlimited = True
    208         if coverage_limit is not None:
    209             request.contribution_limit = coverage_limit
    210         else:
    211             request.contribution_unlimited = True
    212         if multiplier:
    213             request.scalar = multiplier
    214         else:
    215             request.default = True
    216 
    217         def _response_handler(primitive, payload):
    218             status_code = primitive.status_code
    219             status_message = primitive.status_message
    220             if status_code != httplib.OK:
    221                 logger.info('Unexpected error when creating a goal - statusCode: %d, statusMessage: %s', status_code, status_message)
    222                 if status_message == 'at capacity of active goals':
    223                     raise AtGoalCapacity(status_message)
    224                 if 'unable to reserve backing asset for the rewards specified' in status_message:
    225                     if status_code == httplib.BAD_REQUEST:
    226                         raise BadRequestToReserveAsset(status_message)
    227                     elif status_code == httplib.FORBIDDEN:
    228                         raise WalletAccessForbidden(status_message)
    229                     else:
    230                         raise InternalErrorReservingAsset(status_message)
    231                 raise GoalError(status_message)
    232             goal_id = uuid.UUID(bytes=payload.goal.uuid)
    233             logger.info('Goal created successfully - Identifier: %s', repr(goal_id))
    234 
    235         def _timeout_handler(timeout_exception):
    236             logger.error('create_goal timed out: %s' % timeout_exception, exc_info=True)
    237             raise GoalError(timeout_exception)
    238 
    239         logger.info('Sending CreateGoalRequest with data %s', request)
    240         self._send_request_and_wait_for_response(request, CreateResponse, _response_handler, _timeout_handler)
    241 
    242     def close_goal(self, goal_id):
    243         request = CloseRequest()
    244         request.goal.uuid = goal_id.bytes
    245 
    246         def _response_handler(primitive, _payload):
    247             if primitive.status_code != httplib.OK:
    248                 logger.error('unexpected error when closing goal %s - statusCode: %s, statusMessage: %s', goal_id, primitive.status_code, primitive.status_message)
    249                 if primitive.status_code == httplib.NOT_FOUND:
    250                     raise GoalNotFound(goal_id, session.corpid)
    251                 raise GoalError(primitive.status_message)
    252 
    253         def _timeout_handler(timeout_exception):
    254             logger.error('close_goal timed out: %s' % timeout_exception, exc_info=True)
    255             raise GoalError(timeout_exception)
    256 
    257         self._send_request_and_wait_for_response(request, CloseResponse, _response_handler, _timeout_handler)
    258 
    259     def delete_goal(self, goal_id):
    260         request = DeleteRequest()
    261         request.goal.uuid = goal_id.bytes
    262 
    263         def _response_handler(primitive, _payload):
    264             if primitive.status_code != httplib.OK:
    265                 logger.error('unexpected error when deleting goal %s - statusCode: %s, statusMessage: %s', goal_id, primitive.status_code, primitive.status_message)
    266                 if primitive.status_code == httplib.NOT_FOUND:
    267                     raise GoalNotFound(goal_id, session.corpid)
    268                 raise GoalError(primitive.status_message)
    269 
    270         def _timeout_handler(timeout_exception):
    271             logger.error('delete_goal timed out: %s' % timeout_exception, exc_info=True)
    272             raise GoalError(timeout_exception)
    273 
    274         self._send_request_and_wait_for_response(request, DeleteResponse, _response_handler, _timeout_handler)
    275 
    276     def set_manual_goal_progress(self, goal_id, current_progress, updated_progress):
    277         request = SetCurrentProgressRequest()
    278         request.goal.uuid = goal_id.bytes
    279         request.current_progress = current_progress
    280         request.new_progress = updated_progress
    281 
    282         def _response_handler(primitive, _payload):
    283             if primitive.status_code != httplib.OK:
    284                 logger.error('unexpected error when setting progress of goal %s - statusCode: %s, statusMessage: %s', goal_id, primitive.status_code, primitive.status_message)
    285                 if primitive.status_code == httplib.NOT_FOUND:
    286                     raise GoalNotFound(goal_id, session.corpid)
    287                 raise GoalError(primitive.status_message)
    288 
    289         def _timeout_handler(timeout_exception):
    290             logger.error('set_current_progress timed out: %s' % timeout_exception, exc_info=True)
    291             raise GoalError(timeout_exception)
    292 
    293         self._send_request_and_wait_for_response(request, SetCurrentProgressResponse, _response_handler, _timeout_handler)
    294 
    295     def _send_request_and_wait_for_response(self, request, expected_response_class, response_handler, timeout_handler = None):
    296         request_primitive, response_channel = self.public_gateway.send_character_request(request, expected_response_class, UPDATE_TIMEOUT_SECONDS)
    297         try:
    298             response_primitive, payload = response_channel.receive()
    299             response_handler(response_primitive, payload)
    300         except TimeoutException as e:
    301             if timeout_handler is not None:
    302                 timeout_handler(e)
    303 
    304     def _send_request(self, request, response_class, not_found_as_none = False):
    305         logger.info('Sending Request %s:\n%s', request.__class__.__name__, request)
    306         request_primitive, response_channel = self.public_gateway.send_character_request(request, response_class)
    307         response_primitive, payload = response_channel.receive()
    308         if response_primitive.status_code == httplib.OK:
    309             logger.info('Send Request Response %s:\nRequest:\n%s\nResponse:\n%s', request.__class__.__name__, request, payload)
    310             return payload
    311         if not_found_as_none and response_primitive.status_code == httplib.NOT_FOUND:
    312             logger.warning('%s returned with statusCode %s, statusMessage: %s', request.DESCRIPTOR.full_name, response_primitive.status_code, response_primitive.status_message)
    313             return None
    314         logger.error('%s returned with statusCode %s, statusMessage: %s', request.DESCRIPTOR.full_name, response_primitive.status_code, response_primitive.status_message)
    315         raise GoalMessengerError(request.DESCRIPTOR.name, response_primitive.status_code)