Compat overhaul

This commit is contained in:
Roberto Anic Banic
2017-10-16 22:52:58 +02:00
parent ce7f3151eb
commit a97611aee4
8 changed files with 435 additions and 115 deletions

View File

@@ -1,11 +1,15 @@
<component name="ProjectDictionaryState">
<dictionary name="nicba1010">
<words>
<w>appveyor</w>
<w>bluray</w>
<w>channelid</w>
<w>compat</w>
<w>compatlist</w>
<w>hong</w>
<w>hongkong</w>
<w>ingame</w>
<w>requestor</w>
</words>
</dictionary>
</component>

View File

@@ -0,0 +1,9 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="projectProfile" value="Default" />
<option name="useProjectProfile" value="false" />
<option name="PROJECT_PROFILE" value="Default" />
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

183
.idea/workspace.xml generated
View File

@@ -2,13 +2,14 @@
<project version="4">
<component name="ChangeListManager">
<list default="true" id="1e5142f4-a76d-4228-bce7-29ca2474f218" name="Default" comment="">
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/api/ApiConfig.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/api/ApiRequest.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/api/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/api/utils.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.gitignore" afterPath="$PROJECT_DIR$/.gitignore" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/dictionaries/nicba1010.xml" afterPath="$PROJECT_DIR$/.idea/dictionaries/nicba1010.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/api/ApiConfig.py" afterPath="$PROJECT_DIR$/api/ApiConfig.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/api/ApiRequest.py" afterPath="$PROJECT_DIR$/api/ApiRequest.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/api/ApiResponse.py" afterPath="$PROJECT_DIR$/api/ApiResponse.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/api/ApiResult.py" afterPath="$PROJECT_DIR$/api/ApiResult.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/bot.py" afterPath="$PROJECT_DIR$/bot.py" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
@@ -22,10 +23,10 @@
<file leaf-file-name="ApiRequest.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/api/ApiRequest.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1309">
<caret line="77" column="0" lean-forward="true" selection-start-line="77" selection-start-column="0" selection-end-line="77" selection-end-column="0" />
<state relative-caret-position="1354">
<caret line="175" column="8" lean-forward="false" selection-start-line="175" selection-start-column="8" selection-end-line="175" selection-end-column="8" />
<folding>
<element signature="e#0#11#0" expanded="true" />
<element signature="e#26#37#0" expanded="true" />
</folding>
</state>
</provider>
@@ -34,10 +35,10 @@
<file leaf-file-name="ApiResponse.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/api/ApiResponse.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="272">
<caret line="16" column="37" lean-forward="false" selection-start-line="16" selection-start-column="37" selection-end-line="16" selection-end-column="37" />
<state relative-caret-position="587">
<caret line="57" column="53" lean-forward="false" selection-start-line="57" selection-start-column="42" selection-end-line="57" selection-end-column="53" />
<folding>
<element signature="e#0#11#0" expanded="true" />
<element signature="e#26#37#0" expanded="true" />
</folding>
</state>
</provider>
@@ -46,35 +47,37 @@
<file leaf-file-name="ApiResult.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/api/ApiResult.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="204">
<caret line="12" column="26" lean-forward="false" selection-start-line="12" selection-start-column="26" selection-end-line="12" selection-end-column="26" />
<state relative-caret-position="544">
<caret line="32" column="20" lean-forward="true" selection-start-line="32" selection-start-column="20" selection-end-line="32" selection-end-column="20" />
<folding>
<element signature="e#0#29#0" expanded="true" />
<element signature="e#25#54#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name=".gitignore" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="816">
<caret line="105" column="12" lean-forward="false" selection-start-line="105" selection-start-column="12" selection-end-line="105" selection-end-column="12" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="ApiConfig.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/api/ApiConfig.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="646">
<caret line="38" column="1" lean-forward="false" selection-start-line="38" selection-start-column="1" selection-end-line="38" selection-end-column="1" />
<state relative-caret-position="578">
<caret line="34" column="27" lean-forward="false" selection-start-line="34" selection-start-column="18" selection-end-line="34" selection-end-column="27" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="bot.py" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/bot.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="164">
<caret line="56" column="0" lean-forward="true" selection-start-line="56" selection-start-column="0" selection-end-line="56" selection-end-column="0" />
<folding>
<element signature="e#0#11#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
@@ -91,11 +94,12 @@
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/api/__init__.py" />
<option value="$PROJECT_DIR$/api/ApiConfig.py" />
<option value="$PROJECT_DIR$/api/ApiRequest.py" />
<option value="$PROJECT_DIR$/api/ApiResponse.py" />
<option value="$PROJECT_DIR$/api/ApiResult.py" />
<option value="$PROJECT_DIR$/.gitignore" />
<option value="$PROJECT_DIR$/api/ApiResult.py" />
<option value="$PROJECT_DIR$/api/ApiConfig.py" />
<option value="$PROJECT_DIR$/api/ApiResponse.py" />
<option value="$PROJECT_DIR$/api/ApiRequest.py" />
<option value="$PROJECT_DIR$/bot.py" />
</list>
</option>
</component>
@@ -111,6 +115,22 @@
<option name="width" value="1936" />
<option name="height" value="1056" />
</component>
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id />
</State>
</expanded-state>
<selected-state>
<State>
<id>AngularJS</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
@@ -147,7 +167,7 @@
</panes>
</component>
<component name="PropertiesComponent">
<property name="settings.editor.selected.configurable" value="pyconsole" />
<property name="settings.editor.selected.configurable" value="preferences.sourceCode.Python" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="WebServerToolWindowFactoryState" value="true" />
</component>
@@ -166,6 +186,9 @@
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="1e5142f4-a76d-4228-bce7-29ca2474f218" name="Default" comment="" />
@@ -180,25 +203,25 @@
<frame x="-8" y="-8" width="1936" height="1056" extended-state="0" />
<editor active="true" />
<layout>
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32979852" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32979852" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.22905621" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
<window_info id="Remote Host" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="combo" />
<window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32979852" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32979852" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />
<window_info id="Data View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="5" side_tool="false" content_ui="combo" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
</layout>
</component>
@@ -218,7 +241,7 @@
<state relative-caret-position="204">
<caret line="12" column="0" lean-forward="false" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
<folding>
<element signature="e#186#210#0" expanded="true" />
<element signature="e#186#210#0" expanded="false" />
</folding>
</state>
</provider>
@@ -231,52 +254,78 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/ApiConfig.py">
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="646">
<caret line="38" column="1" lean-forward="false" selection-start-line="38" selection-start-column="1" selection-end-line="38" selection-end-column="1" />
<state relative-caret-position="816">
<caret line="105" column="12" lean-forward="false" selection-start-line="105" selection-start-column="12" selection-end-line="105" selection-end-column="12" />
<folding />
</state>
</provider>
</entry>
<entry file="file://C:/Program Files (x86)/Python36-32/Lib/datetime.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="202">
<caret line="1712" column="8" lean-forward="false" selection-start-line="1712" selection-start-column="8" selection-end-line="1712" selection-end-column="8" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.PyCharm2017.2/system/python_stubs/1402888915/builtins.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="306">
<caret line="5109" column="8" lean-forward="false" selection-start-line="5109" selection-start-column="8" selection-end-line="5109" selection-end-column="8" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/ApiResult.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="544">
<caret line="32" column="20" lean-forward="true" selection-start-line="32" selection-start-column="20" selection-end-line="32" selection-end-column="20" />
<folding>
<element signature="e#25#54#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/ApiRequest.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1309">
<caret line="77" column="0" lean-forward="true" selection-start-line="77" selection-start-column="0" selection-end-line="77" selection-end-column="0" />
<state relative-caret-position="1354">
<caret line="175" column="8" lean-forward="false" selection-start-line="175" selection-start-column="8" selection-end-line="175" selection-end-column="8" />
<folding>
<element signature="e#0#11#0" expanded="true" />
<element signature="e#26#37#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/ApiResponse.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="272">
<caret line="16" column="37" lean-forward="false" selection-start-line="16" selection-start-column="37" selection-end-line="16" selection-end-column="37" />
<state relative-caret-position="587">
<caret line="57" column="53" lean-forward="false" selection-start-line="57" selection-start-column="42" selection-end-line="57" selection-end-column="53" />
<folding>
<element signature="e#26#37#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/ApiConfig.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="578">
<caret line="34" column="27" lean-forward="false" selection-start-line="34" selection-start-column="18" selection-end-line="34" selection-end-column="27" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/bot.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="164">
<caret line="56" column="0" lean-forward="true" selection-start-line="56" selection-start-column="0" selection-end-line="56" selection-end-column="0" />
<folding>
<element signature="e#0#11#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/api/ApiResult.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="204">
<caret line="12" column="26" lean-forward="false" selection-start-line="12" selection-start-column="26" selection-end-line="12" selection-end-column="26" />
<folding>
<element signature="e#0#29#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="816">
<caret line="105" column="12" lean-forward="false" selection-start-line="105" selection-start-column="12" selection-end-line="105" selection-end-column="12" />
<folding />
</state>
</provider>
</entry>
</component>
<component name="masterDetails">
<states>

View File

@@ -1,7 +1,60 @@
"""
API Configuration File
"""
version = 1
datetime_input_format = "%Y-%m-%d"
datetime_output_format = "%Y-%m-%d"
datetime_compatlist_query_format = "%Y%m%d"
base_url = "https://rpcs3.net/compatibility"
newline_separator = "<newline>"
return_codes = {
0: {
"display_results": True,
"override_all": False,
"display_footer": True,
"info": "Results successfully retrieved."
},
1: {
"display_results": False,
"override_all": False,
"display_footer": True,
"info": "No results."
},
2: {
"display_results": True,
"override_all": False,
"display_footer": True,
"info": "No match was found, displaying closest results."
},
-1: {
"display_results": False,
"override_all": True,
"display_footer": False,
"info": "{requestor}: Internal error occurred, please contact Ani and Nicba1010"
},
-2: {
"display_results": False,
"override_all": True,
"display_footer": False,
"info": "{requestor}: API is under maintenance, please try again later."
},
-3: {
"display_results": False,
"override_all": False,
"display_footer": False,
"info": "Illegal characters found, please try again with a different search term."
}
}
default_amount = 1
request_result_amount = {
1: 15,
2: 25,
3: 50,
4: 100
}
directions = {
"a": ("a", "asc", "ascending"),
@@ -36,4 +89,4 @@ sort_types = {
release_types = {
"b": ("b", "d", "disc", "bluray"),
"n": ("n", "p", "psn")
}
}

View File

@@ -1,12 +1,22 @@
"""
ApiRequest class
"""
import html
from datetime import datetime
import requests
import api
from api import base_url, datetime_input_format, datetime_compatlist_query_format
from api import base_url, datetime_input_format, datetime_compatlist_query_format, ApiResponse, version
class ApiRequest(object):
def __init__(self):
"""
API Request builder object
"""
def __init__(self) -> None:
self.search = None
self.status = None
self.start = None
@@ -14,11 +24,24 @@ class ApiRequest(object):
self.date = None
self.release_type = None
self.region = None
self.amount = api.default_amount
self.amount_wanted = api.request_result_amount[api.default_amount]
def search(self, string):
self.search = string
def set_search(self, search: str) -> 'ApiRequest':
"""
Adds the search string to the query.
:param search: string to search for
:return: ApiRequest object
"""
self.search = search
return self
def status(self, status):
def set_status(self, status: int) -> 'ApiRequest':
"""
Adds status filter to the query.
:param status: status to filter by, see ApiConfig.statuses
:return: ApiRequest object
"""
try:
self.status = api.statuses[status]
except KeyError:
@@ -26,7 +49,12 @@ class ApiRequest(object):
return self
def startswith(self, start):
def set_startswith(self, start: str) -> 'ApiRequest':
"""
Adds starting character filter to the query.
:param start: character to filter by
:return: ApiRequest object
"""
if len(start) != 1:
if start in ("num", "09"):
self.start = "09"
@@ -37,7 +65,13 @@ class ApiRequest(object):
return self
def sort(self, sort_type, direction):
def set_sort(self, sort_type, direction) -> 'ApiRequest':
"""
Adds sorting request to query.
:param sort_type: element to sort by, see ApiConfig.sort_types
:param direction: sorting direction, see ApiConfig.directions
:return: ApiRequest object
"""
for k, v in api.directions:
if direction in v:
try:
@@ -49,7 +83,12 @@ class ApiRequest(object):
return self
def date(self, date):
def set_date(self, date: str) -> 'ApiRequest':
"""
Adds date filter to query.
:param date: date to filter by
:return: ApiRequest object
"""
try:
date = datetime.strptime(date, datetime_input_format)
self.date = datetime.strftime(date, datetime_compatlist_query_format)
@@ -58,7 +97,12 @@ class ApiRequest(object):
return self
def release_type(self, release_type):
def set_release_type(self, release_type: str) -> 'ApiRequest':
"""
Adds release type filter to query.
:param release_type: release type to filter by, see ApiConfig.release_type
:return: ApiRequest object
"""
for k, v in api.release_types:
if release_type in v:
self.release_type = k
@@ -67,7 +111,12 @@ class ApiRequest(object):
self.release_type = None
return self
def region(self, region):
def set_region(self, region: str) -> 'ApiRequest':
"""
Adds region filter to query.
:param region: region to filter by, see ApiConfig.regions
:return: ApiRequest object
"""
for k, v in api.regions:
if region in v:
self.region = k
@@ -76,8 +125,37 @@ class ApiRequest(object):
self.region = None
return self
def build_query(self):
def set_amount(self, amount: int) -> 'ApiRequest':
"""
Sets the desired result count and gets the closest available.
:param amount: desired result count, chooses closest available option, see ApiConfig.request_result_amount
:return: ApiRequest object
"""
if max(api.request_result_amount, key=api.request_result_amount.get) >= amount >= 1:
current_diff = -1
for k, v in api.request_result_amount:
if v >= amount:
diff = v - amount
if diff < current_diff or current_diff == -1:
self.amount = k
current_diff = diff
if current_diff != -1:
self.amount_wanted = amount
else:
self.amount_wanted = None
self.amount = api.default_amount
return self
def build_query(self) -> str:
"""
Builds the search query.
:return: the search query
"""
url = base_url + "?"
if self.search is not None:
url += "g={}&".format(html.escape(self.search))
if self.status is not None:
@@ -91,5 +169,13 @@ class ApiRequest(object):
if self.release_type is not None:
url += "t={}&".format(self.release_type)
if self.region is not None:
url += "f={}".format(self.region)
return url
url += "f={}&".format(self.region)
return url + "api=v{}".format(version)
def request(self) -> ApiResponse:
"""
Makes an API request to the API with the current request configuration.
:return: the API response
"""
return ApiResponse(requests.get(self.build_query()).content, amount_wanted=self.amount_wanted)

View File

@@ -1,17 +1,73 @@
"""
ApiResponse class
"""
import json
from array import array
from typing import Dict, List
from api import ApiResult
from api import ApiResult, newline_separator, return_codes
class ApiResponse(object):
def __init__(self, data):
self.results = array()
"""
API Response object
"""
def __init__(self, data: Dict, amount_wanted: int = None) -> None:
self.results: List[ApiResult] = array()
parsed_data = json.loads(data)
self.code = parsed_data["return_code"]
self.load_results(parsed_data["results"])
self.load_results(parsed_data["results"], amount=amount_wanted)
def load_results(self, data):
for id, result_data in data:
self.results.append(ApiResult(id, result_data))
def load_results(self, data: Dict, amount: int = None) -> None:
"""
Loads the result object from JSON
:param data: data for the result objects
:param amount: desired amount to load
"""
for game_id, result_data in data:
if amount is None or len(self.results) < amount:
self.results.append(ApiResult(game_id, result_data))
else:
break
def to_string(self) -> str:
"""
Makes a string representation of the object.
:return: string representation of the object
"""
header_string = "{requestor} searched for: {search_term}"
results_string = ""
results_string_part = "```\n"
for result in self.results:
result_string = result.to_string()
if len(results_string) + len(result_string) + 4 > 2000:
results_string_part += "```"
results_string += results_string_part + newline_separator
results_string_part = "```\n"
results_string_part += results_string_part + '\n'
if results_string_part != "```\n":
results_string_part += "```"
results_string += results_string_part
footer_string = "Retrieved from: {request_url}"
if return_codes[self.code]["display_results"]:
return "{}{}{}{}{}".format(
header_string + '\n' + return_codes[self.code]["info"],
newline_separator,
results_string,
newline_separator,
footer_string
)
elif return_codes[self.code]["override_all"]:
return return_codes[self.code]["info"]
else:
return "{}{}".format(
header_string + '\n' + return_codes[self.code]["info"],
(newline_separator + footer_string) if return_codes[self.code]["display_footer"] else ""
)

View File

@@ -1,10 +1,19 @@
"""
ApiResult class
"""
from datetime import datetime
from typing import Dict
from api import datetime_input_format, datetime_output_format, trim_string
class ApiResult(object):
def __init__(self, game_id, data):
"""
API Result object
"""
def __init__(self, game_id: str, data: Dict) -> None:
self.game_id = game_id
self.title = data["title"]
self.status = data["status"]
@@ -13,7 +22,11 @@ class ApiResult(object):
self.commit = data["commit"]
self.pr = data["pr"]
def to_string(self):
def to_string(self) -> str:
"""
Makes a string representation of the object.
:return: string representation of the object
"""
return "ID:{:9s} Title:{:40s} PR:{:4s} Status:{:8s} Updated:{:10s}".format(
self.game_id,
trim_string(self.title, 40),

100
bot.py
View File

@@ -1,16 +1,20 @@
import json
import re
import sys
import urllib.parse
import discord
import requests
import re
import urllib.parse
import json
from bs4 import BeautifulSoup
from discord.ext.commands import Bot
import sys
channelid = "291679908067803136"
from api import ApiRequest, newline_separator
channel_id = "291679908067803136"
rpcs3Bot = Bot(command_prefix="!")
pattern = '[A-z]{4}\\d{5}'
@rpcs3Bot.event
async def on_message(message):
if message.author.name == "RPCS3 Bot":
@@ -28,31 +32,57 @@ async def on_message(message):
codelist.append(code)
print(code)
for code in codelist:
info = await getCode(code)
info = await get_code(code)
if not info == "None":
await rpcs3Bot.send_message(message.channel, info)
@rpcs3Bot.command()
async def credits(*args):
"""Author Credit"""
return await rpcs3Bot.say("```\nMade by Roberto Anic Banic aka nicba1010!\n```")
@rpcs3Bot.command(pass_context=True)
async def c(ctx, *args):
"""Searches the compatibility database, USE: !c searchterm """
await compatSearch(ctx, *args)
await compat_search(ctx, *args)
@rpcs3Bot.command(pass_context=True)
async def compat(ctx, *args):
"""Searches the compatibility database, USE: !compat searchterm"""
await compatSearch(ctx, *args)
async def compat_search(ctx, *args):
search_string = ""
for arg in args:
search_string += arg
request = ApiRequest()
request.set_search(search_string)
response = request.request()
await dispatch_messages(response.to_string().format(
requestor=ctx.message.author.mention,
search_term=search_string,
request_url=request.build_query()
))
async def dispatch_messages(message: str):
for part in message.split(newline_separator):
await rpcs3Bot.send_message(discord.Object(id=channel_id), message)
@rpcs3Bot.command(pass_context=True)
async def latest(ctx, *args):
"""Get the latest RPCS3 build link"""
appveyor_url = BeautifulSoup(requests.get("https://rpcs3.net/download").content, "lxml").find("div", {"class": "div-download-left"}).parent['href']
appveyor_url = BeautifulSoup(requests.get("https://rpcs3.net/download").content, "lxml").find("div", {
"class": "div-download-left"}).parent['href']
return await rpcs3Bot.send_message(ctx.message.author, appveyor_url)
@rpcs3Bot.command(pass_context=True)
async def newest(ctx, *args):
"""Gets the 10 newest updated games"""
@@ -63,7 +93,9 @@ async def newest(ctx, *args):
if ctx.message.channel.id != "319224795785068545":
return
url = "https://rpcs3.net/compatibility?o=4d&r=1&api=v1"
return await search(url, ctx, *args, limit=limit, search_title="{}: Top {} newest tested games!".format(ctx.message.author.mention, limit))
return await search(url, ctx, *args, limit=limit,
search_title="{}: Top {} newest tested games!".format(ctx.message.author.mention, limit))
@rpcs3Bot.command(pass_context=True)
async def oldest(ctx, *args):
@@ -75,7 +107,9 @@ async def oldest(ctx, *args):
if ctx.message.channel.id != "319224795785068545":
return
url = "https://rpcs3.net/compatibility?o=4a&r=1&api=v1"
return await search(url, ctx, *args, limit=limit, search_title="{}: Top {} oldest tested games!".format(ctx.message.author.mention, limit))
return await search(url, ctx, *args, limit=limit,
search_title="{}: Top {} oldest tested games!".format(ctx.message.author.mention, limit))
async def compatSearch(ctx, *args):
print(ctx.message.channel)
@@ -90,45 +124,58 @@ async def compatSearch(ctx, *args):
escapedSearch = escapedSearch[1:]
unescapedSearch = unescapedSearch[1:]
if len(unescapedSearch) < 3:
return await rpcs3Bot.send_message(discord.Object(id=channelid), "{} please use 3 or more characters!".format(ctx.message.author.mention))
return await rpcs3Bot.send_message(discord.Object(id=channel_id),
"{} please use 3 or more characters!".format(ctx.message.author.mention))
url = "https://rpcs3.net/compatibility?g={}&r=1&api=v1".format(escapedSearch)
return await search(url, ctx, *args, query=unescapedSearch)
async def getCode(code):
async def get_code(code):
url = "https://rpcs3.net/compatibility?g={}&r=1&api=v1".format(code)
jsonn = requests.get(url).text
data = json.loads(jsonn)
if data["return_code"] == -2:
return "None"
#return await rpcs3Bot.send_message(discord.Object(id=channelid), "Please be patient API is in maintenance mode!")
# return await rpcs3Bot.send_message(discord.Object(id=channelid), "Please be patient API is in maintenance mode!")
if data["return_code"] == 0:
result_arr = data["results"]
for id, info in result_arr.items():
title = info["title"]
if len(title) > 40:
title = "{}...".format(title[:37])
result = "```\nID:{:9s} Title:{:40s} PR:{:4s} Status:{:8s} Updated:{:10s}\n```".format(id, title, "????" if str(info["pr"]) == "0" else str(info["pr"]), info["status"], info["date"])
result = "```\nID:{:9s} Title:{:40s} PR:{:4s} Status:{:8s} Updated:{:10s}\n```".format(
id, title,
"????" if str(info["pr"]) == "0" else str(info["pr"]),
info["status"],
info["date"])
return result
return "None"
async def search(url, ctx, *args, limit=-1, search_title=None, query=None):
jsonn = requests.get(url).text
data = json.loads(jsonn)
if data["return_code"] == -3:
print(url)
return await rpcs3Bot.send_message(discord.Object(id=channelid), "{}, Illegal search".format(ctx.message.author.mention))
return await rpcs3Bot.send_message(discord.Object(id=channel_id),
"{}, Illegal search".format(ctx.message.author.mention))
if data["return_code"] == -2:
return await rpcs3Bot.send_message(discord.Object(id=channelid), "Please be patient API is in maintenance mode!")
return await rpcs3Bot.send_message(discord.Object(id=channel_id),
"Please be patient API is in maintenance mode!")
if data["return_code"] == -1:
return await rpcs3Bot.send_message(discord.Object(id=channelid), "API Internal Error")
#if data["return_code"] == 2:
return await rpcs3Bot.send_message(discord.Object(id=channel_id), "API Internal Error")
# if data["return_code"] == 2:
# await rpcs3Bot.send_message(discord.Object(id=channelid), ", no result found, displaying alternatives for {}!".format(ctx.message.author.mention, unescapedSearch))
if data["return_code"] == 1:
return await rpcs3Bot.send_message(discord.Object(id=channelid), "{} searched for {} no result found!".format(ctx.message.author.mention, search))
if search_title == None:
await rpcs3Bot.send_message(discord.Object(id=channelid), "{} searched for: {}{}".format(ctx.message.author.mention, query, " " if data["return_code"] == 0 else "\n\tNo results found! Displaying alternatives!"))
return await rpcs3Bot.send_message(discord.Object(id=channel_id),
"{} searched for {} no result found!".format(ctx.message.author.mention,
search))
if search_title is None:
await rpcs3Bot.send_message(discord.Object(id=channel_id),
"{} searched for: {}{}".format(ctx.message.author.mention, query, " " if data[
"return_code"] == 0 else "\n\tNo results found! Displaying alternatives!"))
else:
await rpcs3Bot.send_message(discord.Object(id=channelid), search_title)
await rpcs3Bot.send_message(discord.Object(id=channel_id), search_title)
results = "```"
result_arr = data["results"]
@@ -139,12 +186,15 @@ async def search(url, ctx, *args, limit=-1, search_title=None, query=None):
title = info["title"]
if len(title) > 40:
title = "{}...".format(title[:37])
results += "\nID:{:9s} Title:{:40s} PR:{:4s} Status:{:8s} Updated:{:10s}".format(id, title, "????" if str(info["pr"]) == "0" else str(info["pr"]), info["status"], info["date"])
results += "\nID:{:9s} Title:{:40s} PR:{:4s} Status:{:8s} Updated:{:10s}".format(id, title, "????" if str(
info["pr"]) == "0" else str(info["pr"]), info["status"], info["date"])
if count == limit:
break
results += "\n```"
await rpcs3Bot.send_message(discord.Object(id=channelid), results)
return await rpcs3Bot.send_message(discord.Object(id=channelid), "Retrieved from: {}".format(url.replace("&api=v1", "")))
await rpcs3Bot.send_message(discord.Object(id=channel_id), results)
return await rpcs3Bot.send_message(discord.Object(id=channel_id),
"Retrieved from: {}".format(url.replace("&api=v1", "")))
print(sys.argv[1])
rpcs3Bot.run(sys.argv[1])