4 from datetime
import datetime
5 from decimal
import Decimal
, InvalidOperation
7 from operator
import itemgetter
9 from api
import battle
, citizen
, company
, country
, exchange
, extapi
, feed
, map as erepmap
, market
, region
, utils
10 from cmd_manager
import *
12 from erepublik_utils
import *
14 def command_citizen_register(self
, manager
, opts
, arg
, channel
, sender
):
16 c
= citizen
.from_heapy(int(arg
), by_id
=True) if 'id' in opts
else citizen
.from_heapy(arg
)
18 self
.errormsg(channel
, '%s is not a valid id.' % arg
)
20 except feed
.FeedError
, e
:
21 if e
.msg
== 'Citizen not found or not included in database':
23 self
.errormsg(channel
, 'to link a citizen, use: @b.regnick -i @uyour_id@u@b')
26 c
= citizen
.from_html(int(arg
))
27 except feed
.FeedError
, e
:
28 self
.errormsg(channel
, e
.msg
if e
.msg
!= 'not found.' else 'citizen not found.')
31 self
.errormsg(channel
, e
.msg
)
34 self
.users
.set(sender
, 'citizen', c
.id)
35 self
.msg(channel
, '%s: registered eRepublik citizen @b%s@b' % (sender
, c
.name
))
37 def command_citizen_info(self
, manager
, opts
, arg
, channel
, sender
):
38 c
= self
.get_citizen(opts
, arg
, channel
, sender
)
43 if not c
.is_organization
:
44 position
= 'President' if c
.is_president
else 'Congressman' if c
.is_congressman
else ''
46 message
= u
"""@sep @b{c.name}@b [{c.id}] @sep @bWellness@b {c.wellness} @sep @bStrength@b {c.strength:,.2f} @sep @bRank@b {c.rank[name]} \
47 [{c.rank[id]}] {c.rank_points:,}/{dmg_to_rank:,} points @sep @bLevel@b {c.level} ({c.experience:,} XP) @sep @bAge@b {age:,} days @sep \
48 @bLocation@b {c.region[name]}, {c.country[name]} @sep @bCitizenship@b {c.citizenship[name]}{position}{party}{employer} @sep""".format(
50 dmg_to_rank
= c
.rank
['points'] - 1,
52 position
= ' @sep @bPosition@b %s' % position
if position
else '',
53 party
= ' @sep @bParty@b %s' % (c
.party
['name']) if c
.is_party_member
else '',
54 employer
= u
' @sep @bEmployed at@b {c.employer[name]} [{c.employer[id]}]'.format(c
=c
) if c
.employer
else '')
57 message
= '@sep [Org] @b%(name)s@b [%(id)d] @sep @bLocation@b %(region)s, %(country)s @sep @bAge@b %(age)d days @sep' % {
61 'region' : c
.region
['name'],
62 'country' : c
.country
['name']}
64 self
.msg(channel
, format_citizen_message(c
, message
))
66 def command_citizen_avatar(self
, manager
, opts
, arg
, channel
, sender
):
67 c
= self
.get_citizen(opts
, arg
, channel
, sender
)
74 elif 'medium' in opts
:
75 av
= c
.avatar
.replace('_55x55', '_100x100')
77 av
= c
.avatar
.replace('_55x55', '_142x142')
79 av
= c
.avatar
.replace('_55x55', '')
81 self
.msg(channel
, format_citizen_message(c
, "@sep @b%(name)s@b'%(s)s avatar link @sep %(link)s @sep" % {
83 's' : 's' if c
.name
[-1] != 's' else '',
86 def command_citizen_fightcalc(self
, manager
, opts
, arg
, channel
, sender
):
87 fights
= 1 if 'fights' not in opts
or 'objective' in opts
else opts
['fights']
88 if fights
>= 100000 or fights
== 0:
89 self
.errormsg(channel
, 'fights: expected value (0 <= x <= 100000), got %d instead' % fights
)
92 if 'rank' in opts
and 'strength' in opts
:
95 c
= self
.get_citizen(opts
, arg
, channel
, sender
)
100 if c
.is_organization
:
101 self
.errormsg(channel
, '%s is an organization.' % c
.name
)
104 colors
= [10, 9, 3, 8, 7, 4, 5, 6]
105 rank
= c
.rank
if 'rank' not in opts
else opts
['rank']
106 strength
= c
.strength
if 'strength' not in opts
else opts
['strength']
107 natural_enemy
= True if 'naturalenemy' in opts
else False
109 if 'nextrank' in opts
:
111 self
.errormsg(channel
, 'option -N needs a citizen')
114 natural_enemy
= False # NE bonus removed for training
115 objective
= (c
.rank
['points'] - c
.rank_points
) * 10
116 obj_str
= 'fights required to rank up - {inf:,} '.format(inf
=objective
)
117 elif 'influence' in opts
:
119 self
.errormsg(channel
, 'option -I needs a citizen')
122 if opts
['influence'] >= 10000000000:
123 self
.errormsg(channel
, 'option -I out of range')
126 natural_enemy
= False
127 objective
= 10 * (opts
['influence'] - c
.rank_points
)
129 self
.errormsg(channel
, 'this citizen already has more than {rp:,} rank points'.format(rp
=opts
['influence']))
132 obj_str
= 'fights required to reach {rp:,} rank points, {inf:,} '.format(rp
=opts
['influence'], inf
=objective
)
133 elif 'objective' in opts
:
134 objective
= opts
['objective']
135 if objective
>= 10000000000:
136 self
.errormsg(channel
, 'option -o out of range')
139 obj_str
= 'fights required for {inf:,} '.format(inf
=objective
)
144 damage
= [utils
.fight_calc(q
*20 if q
!= 7 else 200, rank
, strength
, natural_enemy
) for q
in range(8)]
146 damage
= [1 + (objective
/ d
) if objective
% d
!= 0 else objective
/ d
for d
in damage
]
147 damagestr
= ['@c%d[Q%d: @b%d@b]@c' % (z
[0], q
, fights
* z
[1]) for q
, z
in enumerate(zip(colors
, damage
))]
149 message
= '@sep @b%(name)s@b(%(rank)s, %(strength)s strength%(ne)s) %(obj)sinfluence%(fights)s @sep %(damage)s @sep' % {
150 'name' : '%s ' % c
.name
if c
else '',
151 'rank' : '%s [%d]' % (rank
['name'], rank
['id']),
152 'strength': strength
,
153 'ne' : ', against Natural Enemy' if natural_enemy
else '',
155 'fights' : ' in %d fights' % fights
if fights
!= 1 else '',
156 'damage' : ' '.join(damagestr
)}
158 self
.msg(channel
, format_citizen_message(c
, message
))
160 def command_citizen_sscalc(self
, manager
, opts
, arg
, channel
, sender
):
161 natural_enemy
= True if 'naturalenemy' in opts
else False
162 friends
= 0 if 'friends' not in opts
else opts
['friends']
163 if friends
not in [0, 1, 2]:
164 self
.errormsg(channel
, 'Invalid number of friends. Acceptable values: 0, 1, 2.')
167 if 'strength' in opts
:
170 c
= self
.get_citizen(opts
, arg
, channel
, sender
)
175 if c
.is_organization
:
176 self
.errormsg(channel
, '%s is an organization.' % c
.name
)
179 strength
= c
.strength
if 'strength' not in opts
else opts
['strength']
181 bonus
= ['daily reward (+1 str)']
182 bonus
.append('%d friend%s%s' % (friends
, 's' if friends
!= 1 else '', ' (+%s str)' % (friends
* 0.5) if friends
> 0 else ''))
184 next
= (int(strength
/ 250) + 1) * 250
185 diff
= next
- strength
189 septbonus
= 'septemberbonus' in opts
191 inc
+= Decimal('1.5')
192 bonus
.append('September training bonus (+30% str)')
194 if 'climbingcenter' in opts
:
195 cost
+= Decimal('0.19')
196 inc
+= Decimal('2.5')
198 inc
+= Decimal('0.8')
199 bonus
.append('climbing center (+2.5 str, +0.19g)')
200 if 'shootingrange' in opts
:
201 cost
+= Decimal('1.49')
204 inc
+= Decimal('1.5')
205 bonus
.append('shooting range (+5 str, +1.49g)')
206 if 'specialforces' in opts
:
207 cost
+= Decimal('1.79')
211 bonus
.append('special forces (+10 str, +1.79g)')
213 inc
+= Decimal('0.5')
214 bonus
.append('natural enemy (+0.5 str)')
216 inc
+= friends
* Decimal('0.5')
218 days
= int(ceil(diff
/ inc
))
219 total_cost
= cost
* days
220 message
= """@sep {name}({strength:,} / {required:,} strength) days/gold required until next Super Soldier medal @sep @bResult@b {days} days and \
221 {gold:.2f} gold @sep @bBonus@b [+{bonus} / day] {bonus_list} @sep""".format(
222 name
= '@b%s@b ' % c
.name
if c
else '',
228 bonus_list
= ', '.join(bonus
))
229 self
.msg(channel
, format_citizen_message(c
, message
))
231 def command_citizen_link(self
, manager
, opts
, arg
, channel
, sender
):
232 c
= self
.get_citizen(opts
, arg
, channel
, sender
)
237 if not c
.is_organization
:
238 message
= '@sep @b%(name)s@b profile @sep http://www.erepublik.com/en/citizen/profile/%(id)d @sep' % {
239 'name' : format_name(c
.name
),
242 message
= '@sep @b%(name)s@b profile @sep http://economy.erepublik.com/en/accounts/%(id)d @sep' % {
243 'name' : format_name(c
.name
),
246 self
.msg(channel
, format_citizen_message(c
, message
))
248 def command_citizen_donate(self
, manager
, opts
, arg
, channel
, sender
):
249 c
= self
.get_citizen(opts
, arg
, channel
, sender
)
254 self
.msg(channel
, format_citizen_message(c
, """@sep @b%(name)s@b donations @sep @bItems@b http://www.erepublik.com/en/economy/donate-items/%(id)d @sep \
255 @bMoney@b http://www.erepublik.com/en/economy/donate-money/%(id)d @sep""") % {
256 'name' : format_name(c
.name
),
259 def command_citizen_message(self
, manager
, opts
, arg
, channel
, sender
):
260 c
= self
.get_citizen(opts
, arg
, channel
, sender
)
265 self
.msg(channel
, format_citizen_message(c
, '@sep @b%(name)s@b message link @sep http://www.erepublik.com/en/main/messages-compose/%(id)d @sep') % {
266 'name' : format_name(c
.name
),
269 def command_citizen_medals(self
, manager
, opts
, arg
, channel
, sender
):
270 c
= self
.get_citizen(opts
, arg
, channel
, sender
, False)
278 for medal
in sorted(c
.medals
, key
= lambda medal
: c
.medals
[medal
], reverse
= True):
279 medals
.append('@b%d@bx %s' % (c
.medals
[medal
], medal
.title()))
280 total
+= c
.medals
[medal
]
282 message
= '@sep @b%(name)s@b @sep @bMedals@b %(count)d%(medals)s @sep' % {
285 'medals': ' @sep %s' % ' @sep '.join(medals
) if total
> 0 else ''}
287 self
.msg(channel
, format_citizen_message(c
, message
))
289 def command_citizen_rankings(self
, manager
, opts
, arg
, channel
, sender
):
290 c
= self
.get_citizen(opts
, arg
, channel
, sender
, no_api_request
=True)
297 rankings
= extapi
.get_rankings(c
['id'])
299 rankings
= extapi
.get_rankings(c
['name'])
300 except feed
.FeedError
, e
:
301 self
.errormsg(channel
, e
.msg
)
304 if 'error' in rankings
:
305 self
.errormsg(channel
, rankings
['error'])
306 self
.elog
.warning('[warning] 1way API fail: %s' % rankings
['error'])
309 country
= rankings
['country_rankings']
310 world
= rankings
['world_rankings']
312 def format_ranking(source
):
313 return '@bStrength@b {str} ({cstr:,.2f}) @sep @bRank points@b {rp} ({r[rp]:,}) @sep @bQ5 Influence@b {inf} ({r[inf]:,}) @sep @bXP@b {xp} ({r[xp]:,})'.format(
315 str = format_ordinal(source
['strength'], add_thousands_sep
=True),
316 cstr
= Decimal(rankings
['str']),
317 rp
= format_ordinal(source
['rank_points'], add_thousands_sep
=True),
318 inf
= format_ordinal(source
['influence'], add_thousands_sep
=True),
319 xp
= format_ordinal(source
['level'], add_thousands_sep
=True))
321 self
.msg(channel
, format_citizen_message(rankings
, u
"""@sep @b{r[name]}@b [{r[id]}] rankings in @b{country_name}@b ({country[total]:,} citizens) @sep {ranking_str} @sep""".format(
324 ranking_str
= format_ranking(country
),
325 country_name
= erepmap
.get_country_name(rankings
['cid'])), rankings
['sex']))
326 self
.msg(channel
, format_citizen_message(rankings
, u
"""@sep @b{r[name]}@b [{r[id]}] @bglobal@b rankings ({world[total]:,} citizens) @sep {ranking_str} @sep""".format(
329 ranking_str
= format_ranking(world
)), rankings
['sex']))
331 def command_market_info(self
, manager
, opts
, arg
, channel
, sender
):
332 regex
= re
.compile("^(?:(\D+?)\s(\D+?))(?:\s([1-6]))?$")
333 r
= regex
.search(arg
)
335 if r
== None or len(r
.groups()) != 3:
336 self
.usagemsg(channel
, '.market INDUSTRY COUNTRY QUALITY', ['.market food usa 2', '.market grain italy'])
339 industry
, country
, quality
= r
.groups()
344 quality
= int(quality
)
347 industry
= utils
.get_industry(industry
)
348 except utils
.InvalidValueError
, err
:
349 self
.errormsg(channel
, str(err
))
352 if quality
> 5 and industry
['id'] != 1 and industry
['id'] != 2:
353 self
.errormsg(channel
, 'quality parameter out of range (1-5)')
357 if country
!= 'world':
358 country_id
= erepmap
.get_country_id(country
)
359 if country_id
== None:
360 self
.errormsg(channel
, 'country \'%s\' not found.' % country
)
362 country_name
= erepmap
.get_country_name(country_id
)
365 country_name
= 'World'
366 except feed
.FeedError
, e
:
367 self
.errormsg(channel
, e
.msg
)
371 m
= market
.get(industry
, country_id
, quality
)
372 except feed
.FeedError
, e
:
373 self
.errormsg(channel
, e
.msg
)
378 currency
= erepmap
.get_country_currency(best
['country'])
380 self
.msg(channel
, u
"""@sep Best offer for {quality}@b{product}@b in @b{country}@b @sep @bPrice@b {off[price]} {currency} ({off[goldprice]} GOLD) \
381 {off[amount]} available @sep @bLink@b {link} @sep @bLast update@b {last_update} ago @sep""".format(
382 quality
= 'Q%d ' % quality
if industry
['domain']['name'] != 'Land' else '',
383 product
= industry
['name'],
384 country
= country_name
,
387 link
= 'http://economy.erepublik.com/en/market/%d/%d/%d' % (best
['country'], industry
['market_id'], quality
),
388 last_update
= get_timespan(datetime
.fromtimestamp(best
['last_updated_ts']))))
390 self
.msg(channel
, 'No products available on the market with selected attributes.')
392 def command_region_info(self
, manager
, opts
, arg
, channel
, sender
):
393 r
= self
.get_region(opts
, arg
, channel
, sender
)
398 self
.msg(channel
, '@sep @b%(region)s, %(country)s@b [%(id)d] @sep @bPopulation@b %(pop)d%(raw)s%(cons)s @sep' % {
400 'country' : r
.country
['name'],
402 'pop' : r
.population
,
403 'raw' : ' @sep @bResource@b %s' % r
.raw_materials
if r
.raw_materials
else '',
404 'cons' : ' @sep @bConstructions@b%s%s' % (' %dx hospital%s' % (len(r
.hospitals
), 's' if len(r
.hospitals
) != 1 else '') if r
.hospitals
!= 0 else '', ' %dx defense system%s' % (len(r
.defense
), 's' if len(r
.defense
) != 1 else '') if r
.defense
!= 0 else '') if len(r
.hospitals
) != 0 and len(r
.defense
) != 0 else ''})
406 def command_region_borders(self
, manager
, opts
, arg
, channel
, sender
):
407 r
= self
.get_region(opts
, arg
, channel
, sender
)
414 for n
in r
.neighbours
:
415 country
= n
['country']['name']
416 reg
= n
['region']['name']
418 if country
in neighbors
:
419 neighbors
[country
].append(reg
)
421 neighbors
[country
] = [reg
]
423 self
.msg(channel
, '@sep Borders of @b%(region)s, %(country)s@b @sep %(borders)s @sep' % {
425 'country' : r
.country
['name'],
426 'borders' : ' @sep '.join(['@b%s:@b %s' % (n
, ', '.join(neighbors
[n
])) for n
in neighbors
])})
428 def command_region_distance(self
, manager
, opts
, arg
, channel
, sender
):
430 start
, end
= arg
.split(',', 1)
432 start
, end
= None, arg
435 self
.errormsg(channel
, 'region not found')
441 c
= self
.get_citizen(opts
, '', channel
, sender
)
444 start
= c
.region
['name']
446 start_zone
= erepmap
.get_region_zone(start
.strip())
448 self
.errormsg(channel
, 'region not found')
451 end_country
= erepmap
.get_country_id(end
.strip())
453 end_zone
= erepmap
.get_region_zone(end
.strip()) # not a country, check to see if it's a region name
454 if not end_zone
: # not even a region
455 self
.errormsg(channel
, 'region not found')
457 else: # it's a region, calculate path normally
458 distance
= erepmap
.get_region_distance(start_zone
, end_zone
)
459 self
.msg(channel
, '@sep {start[name]} [{szone}] -> {end[name]} [{ezone}] @sep @bDistance@b {dist} zones @sep @bCost@b {cost} currency units @sep'.format(
460 start
= erepmap
.get_region(start
.strip()),
461 end
= erepmap
.get_region(end
.strip()),
467 else: # get all regions in this country, check shortest path for each region in a different zone
468 country_regions
= country
.get_regions(end_country
)
469 country_regions
= country_regions
['regions']
470 paths
= [] # [{'zone': zone, 'regions': regions in this zone, iterator group, 'cost': movement cost}, ...]
471 for zone
, zone_regions
in itertools
.groupby(sorted(country_regions
, key
=itemgetter('zone')), itemgetter('zone')):
472 paths
.append({'zone': zone, 'cost': erepmap.get_region_distance(start_zone, zone), 'regions': list(zone_regions)}
)
474 cheapest
= sorted(paths
, key
=itemgetter('cost'))
476 cheapest
= cheapest
[0]
477 self
.msg(channel
, '@sep {start[name]} [{szone}] -> @b{endcountry}@b ({end[name]}) [{ezone}] @sep @bDistance@b {dist} zones @sep @bCost@b {cost} currency units @sep'.format(
478 start
= erepmap
.get_region(start
.strip()),
479 end
= random
.choice(cheapest
['regions']),
480 endcountry
= erepmap
.get_country_name(end_country
),
482 ezone
= cheapest
['zone'],
483 dist
= cheapest
['cost'],
484 cost
= 20 * cheapest
['cost']
487 self
.errormsg(channel
, '@b%s@b has no regions' % erepmap
.get_country_name(end_country
))
489 def command_country_info(self
, manager
, opts
, arg
, channel
, sender
):
492 country_id
= int(arg
)
494 self
.errormsg(channel
, '%s is not a valid id.' % arg
)
497 country_id
= erepmap
.get_country_id(arg
)
500 self
.errormsg(channel
, 'country @b%s@b not found.' % arg
)
504 countryfeed
= country
.from_id(country_id
)
505 except feed
.FeedError
as e
:
506 self
.errormsg(channel
, e
.msg
)
509 if 'error' in countryfeed
:
510 self
.errormsg(channel
, countryfeed
['error'])
511 self
.elog
.warning('[warning] 1way API fail: %s' % countryfeed
['error'])
514 if len(countryfeed
['citizenships']) > 4:
515 cits
= countryfeed
['citizenships'][:3]
516 cits
.append({'amount': sum(map(itemgetter('amount'), countryfeed['citizenships'][3:])), 'name': 'other countries'}
)
518 cits
= countryfeed
['citizenships']
520 if len(countryfeed
['residents']) > 4:
521 resids
= countryfeed
['residents'][:3]
522 resids
.append({'amount': sum(map(itemgetter('amount'), countryfeed['residents'][3:])), 'name': 'other countries'}
)
524 resids
= countryfeed
['residents']
526 self
.msg(channel
, u
"""@sep @b{country[name]}@b [{country[id]}] @sep @bCapital@b {capital} \
527 @sep @bCitizenships@b {country[citizenships_total]:,} [{citizenships}] @sep @bResidents@b {country[residents_total]:,}{residents} \
528 @sep @bLink@b http://www.erepublik.com/en/country/society/{country[link]} @sep""".format(
529 country
= countryfeed
,
530 capital
= countryfeed
['capital']['name'] if countryfeed
['capital'] else 'None',
531 citizenships
= ', '.join(['{percent:.1%} in {country}'.format(percent
= c
['amount'] / float(countryfeed
['citizenships_total']), country
= c
['name']) for c
in cits
]),
532 residents
= (' [%s]' % ', '.join(['{percent:.1%} from {country}'.format(percent
= c
['amount'] / float(countryfeed
['residents_total']), country
= c
['name']) for c
in resids
])) if resids
else ''))
535 def command_country_resources(self
, manager
, opts
, arg
, channel
, sender
):
538 country_id
= int(arg
)
540 self
.errormsg(channel
, '%s is not a valid id.' % arg
)
543 country_id
= erepmap
.get_country_id(arg
)
546 self
.errormsg(channel
, 'country @b%s@b not found.' % arg
)
550 regions
= country
.get_regions(country_id
)
551 except feed
.FeedError
as e
:
552 self
.errormsg(channel
, e
.msg
)
555 if 'error' in regions
:
556 self
.errormsg(channel
, regions
['error'])
557 self
.elog
.warning('[warning] 1way API fail: %s' % regions
['error'])
560 country_info
= regions
['country']
561 region_list
= regions
['regions']
563 frm
= [7, 8, 9, 10, 11]
564 wrm
= [12, 13, 14, 15, 16]
569 for region
in itertools
.ifilter(itemgetter('is_connected'), region_list
):
570 bonus
= region
['resource']['id']
572 food_regions
.append(region
)
574 weapon_regions
.append(region
)
576 food_regions
= dict((key
['name'], list(group
)) for (key
, group
) in itertools
.groupby(sorted(food_regions
, key
=itemgetter('resource')), itemgetter('resource')))
577 weapon_regions
= dict((key
['name'], list(group
)) for (key
, group
) in itertools
.groupby(sorted(weapon_regions
, key
=itemgetter('resource')), itemgetter('resource')))
579 self
.msg(channel
, """@sep @b{country[name]}@b resources @sep @bFood@b +{food_bonus}%{food_list} @sep @bWeapons@b +{weapons_bonus}%{weapons_list} @sep \
580 @bLink@b http://www.erepublik.com/en/country/economy/{country[link]} @sep""".format(
581 country
= country_info
,
582 food_bonus
= 20 * len(food_regions
),
583 weapons_bonus
= 20 * len(weapon_regions
),
584 food_list
= (' (%s)' % ', '.join(['%dx %s' % (len(regions
), key
) for key
, regions
in sorted(food_regions
.iteritems())])) if food_regions
else '',
585 weapons_list
= (' (%s)' % ', '.join(['%dx %s' % (len(regions
), key
) for key
, regions
in sorted(weapon_regions
.iteritems())])) if weapon_regions
else ''
588 def command_country_tax(self
, manager
, opts
, arg
, channel
, sender
):
589 sp
= arg
.split(' ', 1)
591 self
.usagemsg(channel
, '.tax industry country', ['.tax food italy'])
594 industry
, country_name
= sp
596 industry
= utils
.get_industry(industry
)
597 except utils
.InvalidValueError
, err
:
598 self
.errormsg(channel
, str(err
))
601 country_id
= erepmap
.get_country_id(country_name
)
603 self
.errormsg(channel
, 'invalid country name')
606 res
= country
.get_taxes(country_id
)
607 tax
= itertools
.ifilter(lambda x
: x
['industry']['id'] == industry
['market_id'], res
['taxes']).next()
608 self
.msg(channel
, """@sep @bTaxes@b in {country_name} @sep @bIndustry@b {tax[industry][name]} @sep @bIncome@b {tax[income]}% @sep @bImport@b {tax[import]}% @sep \
609 @bVAT@b {tax[vat]}% @sep @bLast updated@b {last_update} ago @sep""".format(
610 country_name
= res
['country']['name'],
612 last_update
= get_timespan(datetime
.fromtimestamp(res
['last_updated_ts']))))
614 def command_country_mpp(self
, manager
, opts
, arg
, channel
, sender
):
615 if arg
== '' and not 'id' in opts
:
616 citizen
= self
.get_citizen(opts
, arg
, channel
, sender
)
621 arg
= citizen
.country
['id']
625 id = int(arg
) if 'id' in opts
else erepmap
.get_country_id(arg
)
627 self
.errormsg(channel
, '%s is not a valid id.' % arg
)
628 except feed
.FeedError
, e
:
629 self
.errormsg(channel
, e
.msg
)
633 self
.errormsg(channel
, 'country not found.')
636 name
= erepmap
.get_country_name(id)
638 self
.errormsg(channel
, 'country not found.')
642 mpps
= erepmap
.get_mpp_list(id)
643 except feed
.FeedError
, e
:
644 self
.errormsg(channel
, e
.msg
)
647 message
= ['@b%s@b %s' % (mpp
['country'], '%s/%s' % (mpp
['expiration'][6:8], mpp
['expiration'][4:6])) for mpp
in mpps
]
651 self
.multimsg(channel
, 15, '@sep @b%(name)s@b @sep %(count)s MPP%(s)s @sep ' % {
654 's' : 's' if count
!= 1 else ''},
655 ' @sep ', message
, ' @sep ')
657 self
.msg(channel
, '@sep @b%(name)s@b has no active MPPs @sep' % {'name': name}
)
659 def command_battle_info(self
, manager
, opts
, arg
, channel
, sender
):
663 self
.errormsg(channel
, '%s is not a valid battle id.' % arg
)
667 b
= battle
.get_battle(battle_id
)
668 except feed
.FeedError
as e
:
669 self
.errormsg(channel
, e
.msg
)
673 self
.msg(channel
, '@sep @bBattle [{b.id}]@b {b.region[name]} @sep {attacker}@b vs @b{b.defender[name]}@b @sep @bWinner@b {winner} @sep @bEnded@b {b.finished_at} @sep'.format(
675 attacker
= '@b' + b
.attacker
['name'] if not b
.is_resistance
else 'Resistance force of @b%s' % b
.attacker
['name'],
676 winner
= erepmap
.get_country_name(b
.winner_id
)))
678 domination
= b
.status
['domination']
681 elif domination
< 50:
685 self
.msg(channel
, u
"""@sep @bBattle [{b.id}]@b {b.region[name]} @sep {attacker}@b vs @b{b.defender[name]}@b @sep \
686 {status} @sep @bScore@b {b.status[attacker_points]} to \
687 {b.status[defender_points]} @sep @bTime@b {b.status[elapsed_time]} minutes @sep @bLink@b http://www.erepublik.com/en/military/battlefield/{b.id} @sep""".format(
689 attacker
= '@b' + b
.attacker
['name'] if not b
.is_resistance
else 'Resistance force of @b%s' % b
.attacker
['name'],
690 status
= '@bDomination@b {wallatt:.2f}% {separator} {domination:.2f}%'.format(
691 wallatt
= 100 - domination
,
692 separator
= separator
,
693 domination
= domination
) if b
.status
['attacker_points'] < 1800 and b
.status
['defender_points'] < 1800 else
694 '@b{winner}@b won this round @sep @bStatus@b pending'.format(winner
= b
.attacker
['name'] if b
.status
['attacker_points'] >= 1800 else b
.defender
['name'])
697 def command_battle_list(self
, manager
, opts
, arg
, channel
, sender
):
699 battles
= erepmap
.get_battles(arg
)
700 except feed
.FeedError
, e
:
701 self
.errormsg(channel
, e
.msg
)
704 message
= [u
'[{b[battle_id]}] {b[attacker]} vs {b[defender]} in {b[region]}'.format(b
=b
) for b
in sorted(battles
, key
=itemgetter('battle_id'))]
706 if len(message
) == 0:
707 self
.notice(sender
, 'There are no active battles matching your search.')
709 self
.multinotice(sender
, 6, '@sep @bActive battles@b%s @sep ' % (' [%s]' % arg
if arg
!= '' else ''), ' @sep ', message
, ' @sep ')
711 def command_mass(self
, manager
, opts
, arg
, channel
, sender
):
712 if not self
.channels
[channel
].mass
:
713 self
.notice(sender
, 'The .mass command is disabled for this channel. To enable it, type @b/msg eRepublik chanmass %s@b (founder only).' % channel
)
716 chan
= self
.inter
.findChannel(channel
)
717 senderinfo
= self
.inter
.findUser(sender
)
718 if not chan
or not senderinfo
:
721 sender_modes
= chan
.getModes(senderinfo
.getNick())
722 if '@' not in sender_modes
and '&' not in sender_modes
and '~' not in sender_modes
:
723 self
.notice(sender
, 'This command is only available to channel ops, admins and founders.')
726 members
= chan
.getUsers() # Set<String>
727 it
= members
.iterator()
732 u
= self
.inter
.findUser(name
)
733 if not u
or u
['server'].getName() in ['services.rizon.net', 'geo.rizon.net', 'py3.rizon.net']:
737 if not self
.users
.is_valid(user
) or self
.users
[user
].mass
:
738 userlist
.append(user
)
742 while len(userlist
) > 0:
744 out
+= ' ' + userlist
.pop()
746 out
+= userlist
.pop()
748 self
.msg(channel
, '@sep @bMass@b @sep %s @sep' % out
)
750 self
.msg(channel
, '@sep @bMass@b @sep %s @sep' % out
)
751 self
.msg(channel
, '@sep @bMass@b%s @sep %s @sep' % (' [@c4%s@c]' % arg
if arg
else '', 'To opt-out from being highlighted in .mass, type @b/msg eRepublik nousermass@b'))
753 def command_erepublik_info(self
, manager
, opts
, arg
, channel
, sender
):
754 self
.notice(sender
, '@sep @bRizon eRepublik Bot@b @sep @bDevelopers@b ElChE and martin @sep @bHelp/feedback@b %(channel)s @sep' % {
755 'channel' : '#fishbot'})
757 def command_erepublik_help(self
, manager
, opts
, arg
, channel
, sender
):
758 command
= arg
.lower()
761 message
= ['erepublik: .help erepublik - for erepublik commands']
762 elif command
== 'erepublik':
763 message
= manager
.get_help()
765 message
= manager
.get_help(command
)
768 message
= ['%s is not a valid command.' % arg
]
771 self
.notice(sender
, line
)
773 id_opt
= ('id', '-i', 'look up by id instead of name', {'action': 'store_true'}
, ARG_YES
)
774 nick_opt
= ('nick', '-n', 'look up by irc nick', {'action': 'store_true'}
, ARG_YES
)
775 heapy_opt
= ('heapy', '-h', 'use a custom API, not live data (might be a few days old) [use when erep API is offline]', {'action': 'store_true'}
, ARG_OFFLINE
)
777 class UserCommandManager(CommandManager
):
778 def get_prefix(self
):
781 def get_commands(self
):
783 'regcit': 'register_citizen',
784 'regnick': 'register_citizen',
785 'register_citizen': (command_citizen_register
, ARG_YES
, 'links an eRepublik citizen to an IRC nickname', [
790 'lookup': (command_citizen_info
, ARG_OPT
, 'looks up a citizen', [
797 'fightcalc': (command_citizen_fightcalc
, ARG_OPT
, 'calculates influence done with different weapon qualities', [
801 ('strength', '-s', 'uses a custom strength', {'type': '+decimal'}
, ARG_OFFLINE|ARG_OFFLINE_REQ
),
802 ('rank', '-r', 'uses a custom military rank (use rank ID, not name)', {'type': 'rank'}
, ARG_OFFLINE|ARG_OFFLINE_REQ
),
803 ('fights', '-f', 'specifies number of fights', {'type': '+integer'}
, ARG_OFFLINE
),
804 ('naturalenemy', '-e', 'adds 10% natural enemy bonus to influence', {'action': 'store_true'}
, ARG_OFFLINE
),
805 ('nextrank', '-N', 'calculates how many fights are required to rank up', {'action': 'store_true'}
),
806 ('objective', '-o', 'calculates how many fights are required to make a given amount of influence', {'type': '+integer'}
, ARG_OFFLINE
),
807 ('influence', '-I', 'calculates how many fights are required to reach a given amount of rank points', {'type': '+integer'}
, ARG_OPT
)
811 'sscalc': (command_citizen_sscalc
, ARG_OPT
, 'calculates days/gold required for the next SuperSoldier medal', [
815 ('strength', '-s', 'uses a custom strength', {'type': '+decimal'}
, ARG_OFFLINE|ARG_OFFLINE_REQ
),
816 ('friends', '-f', 'how many friends used in training (0, 1, 2)', {'type': '+integer'}
, ARG_OFFLINE
),
817 ('climbingcenter', '-1', 'adds climbing center bonus to training (+2.5 points)', {'action': 'store_true'}
, ARG_OFFLINE
),
818 ('shootingrange', '-2', 'adds shooting range bonus to training (+5 points)', {'action': 'store_true'}
, ARG_OFFLINE
),
819 ('specialforces', '-3', 'adds special forces bonus to training (+10 points)', {'action': 'store_true'}
, ARG_OFFLINE
),
820 ('naturalenemy', '-e', 'adds natural enemy bonus to training (+0.5 points)', {'action': 'store_true'}
, ARG_OFFLINE
),
821 ('septemberbonus', '-b', 'adds +30% bonus to daily training (September\'s bonus)', {'action': 'store_true'}
, ARG_OFFLINE
)
824 'link': (command_citizen_link
, ARG_OPT
, "link to a citizen's profile", [
830 'donate': (command_citizen_donate
, ARG_OPT
, 'link to the donation page of a citizen', [
836 'message': (command_citizen_message
, ARG_OPT
, 'link to send a message to a citizen', [
842 'avatar': (command_citizen_avatar
, ARG_OPT
, 'link to a citizen\'s avatar', [
846 ('small', '-s', 'links to the small sized avatar (55x55px)', {'action': 'store_true'}
, ARG_OPT
),
847 ('medium', '-m', 'links to the medium sized avatar (100x100px)', {'action': 'store_true'}
, ARG_OPT
),
848 ('large', '-l', 'links to the large sized avatar (142x142px)', {'action': 'store_true'}
, ARG_OPT
)
851 'medals': (command_citizen_medals
, ARG_OPT
, 'medals owned by a citizen', [
857 # 'ranking': 'rankings',
858 # 'rankings': (command_citizen_rankings, ARG_OPT|ARG_OFFLINE, 'shows a citizen\'s ranking position', [
863 'market': 'bestprice',
865 'bestprice': (command_market_info
, ARG_YES|ARG_OFFLINE
, 'shows the best offer in the selected market', [], 'industry country quality'),
867 'region': (command_region_info
, ARG_OPT
, 'information about a region', [
871 'borders': (command_region_borders
, ARG_OPT
, 'list of regions borders', [
876 'distance': (command_region_distance
, ARG_YES
, 'calculates zones-distance between two regions', [], 'from_region, to_region'),
878 # 'country': (command_country_info, ARG_YES|ARG_OFFLINE, 'information about a country', [
880 # ], 'country_name'),
883 'resource': 'resources',
884 'resources': (command_country_resources
, ARG_YES|ARG_OFFLINE
, "summary of a country's resources", [
888 'tax': (command_country_tax
, ARG_OPT|ARG_OFFLINE
, 'information about a country', [], 'country industry'),
891 'mpps': (command_country_mpp
, ARG_OPT
, 'shows a country\'s active mpp list and their expiration date', [
895 'battle': (command_battle_info
, ARG_YES
, 'information about a battle', [], 'battle_id'),
897 'battles': (command_battle_list
, ARG_OPT
, 'shows a list of active battles', []),
899 'mass': (command_mass
, ARG_OPT
, 'highlights all the nicks in the channel (limited to channel ops, admins and founders)', [], 'optional_message'),
901 'info': (command_erepublik_info
, ARG_NO|ARG_OFFLINE
, 'Displays version and author information', []),
902 'help': (command_erepublik_help
, ARG_OPT|ARG_OFFLINE
, 'Displays available commands and their usage', []),