├── .coveragerc ├── .gitignore ├── Makefile ├── README.rst ├── docs ├── .gitignore ├── Makefile ├── conf.py ├── dota2.client.rst ├── dota2.enums.rst ├── dota2.features.chat.rst ├── dota2.features.lobby.rst ├── dota2.features.match.rst ├── dota2.features.party.rst ├── dota2.features.player.rst ├── dota2.features.rst ├── dota2.features.sharedobjects.rst ├── dota2.msg.rst ├── dota2.rst ├── dota2.utils.rst ├── index.rst └── user_guide.rst ├── dota2 ├── __init__.py ├── client.py ├── common_enums.py ├── enums.py ├── features │ ├── __init__.py │ ├── chat.py │ ├── lobby.py │ ├── match.py │ ├── party.py │ ├── player.py │ └── sharedobjects.py ├── msg.py ├── proto_enums.py ├── protobufs │ ├── __init__.py │ ├── base_gcmessages_pb2.py │ ├── dota_client_enums_pb2.py │ ├── dota_gcmessages_client_chat_pb2.py │ ├── dota_gcmessages_client_fantasy_pb2.py │ ├── dota_gcmessages_client_guild_pb2.py │ ├── dota_gcmessages_client_match_management_pb2.py │ ├── dota_gcmessages_client_pb2.py │ ├── dota_gcmessages_client_team_pb2.py │ ├── dota_gcmessages_client_tournament_pb2.py │ ├── dota_gcmessages_client_watch_pb2.py │ ├── dota_gcmessages_common_match_management_pb2.py │ ├── dota_gcmessages_common_pb2.py │ ├── dota_gcmessages_msgid_pb2.py │ ├── dota_match_metadata_pb2.py │ ├── dota_shared_enums_pb2.py │ ├── econ_gcmessages_pb2.py │ ├── econ_shared_enums_pb2.py │ ├── gcsdk_gcmessages_pb2.py │ ├── gcsystemmsgs_pb2.py │ └── steammessages_pb2.py └── utils │ └── __init__.py ├── gen_enum_from_protos.py ├── protobuf_list.txt ├── protobufs ├── base_gcmessages.proto ├── dota_client_enums.proto ├── dota_gcmessages_client.proto ├── dota_gcmessages_client_chat.proto ├── dota_gcmessages_client_fantasy.proto ├── dota_gcmessages_client_guild.proto ├── dota_gcmessages_client_match_management.proto ├── dota_gcmessages_client_team.proto ├── dota_gcmessages_client_tournament.proto ├── dota_gcmessages_client_watch.proto ├── dota_gcmessages_common.proto ├── dota_gcmessages_common_match_management.proto ├── dota_gcmessages_msgid.proto ├── dota_match_metadata.proto ├── dota_shared_enums.proto ├── econ_gcmessages.proto ├── econ_shared_enums.proto ├── gcsdk_gcmessages.proto ├── gcsystemmsgs.proto ├── google │ └── protobuf │ │ └── descriptor.proto └── steammessages.proto ├── requirements.txt └── setup.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | concurrency = gevent 3 | omit = 4 | dota2/protobufs/* 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | *.egg-info 3 | *.pyc 4 | .coverage 5 | *.swp 6 | 7 | dota2/protobufs/*.proto 8 | build 9 | venv 10 | *.log 11 | .idea 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | define HELPBODY 2 | Available commands: 3 | 4 | make help - this thing. 5 | 6 | make init - install python dependancies 7 | make test - run tests and coverage 8 | make pylint - code analysis 9 | make build - pylint + test 10 | make docs - generate html docs using sphinx 11 | 12 | make dist - build source distribution 13 | mage register - register in pypi 14 | make upload - upload to pypi 15 | 16 | make pb_fetch - fetch protobufs from SteamRE 17 | make pb_compile - compile with protoc 18 | make pb_clear - removes *.proto 19 | make pb_update - pb_fetch + pb_compile 20 | 21 | endef 22 | 23 | export HELPBODY 24 | help: 25 | @echo "$$HELPBODY" 26 | 27 | init: 28 | pip install -r requirements.txt 29 | 30 | test: 31 | coverage erase 32 | PYTHONHASHSEED=0 nosetests --verbosity 1 --with-coverage --cover-package=dota2 33 | 34 | pylint: 35 | pylint -r n -f colorized dota2 || true 36 | 37 | build: pylint test docs 38 | 39 | .FORCE: 40 | docs: .FORCE 41 | $(MAKE) -C docs html 42 | 43 | clean: 44 | rm -rf dist dota2.egg-info dota2/*.pyc 45 | 46 | dist: clean 47 | python setup.py sdist 48 | 49 | register: 50 | python setup.py register -r pypi 51 | 52 | upload: dist register 53 | twine upload -r pypi dist/* 54 | 55 | pb_fetch: 56 | wget -nv --backups=1 --show-progress -P ./protobufs/ -i protobuf_list.txt 57 | rm -f ./protobufs/*.1 58 | sed -i '1s/^/syntax = "proto2"\;\npackage dota\;\n/' protobufs/*.proto 59 | sed -i 's/\(optional\|repeated\) \.\([A-Z]\)/\1 dota.\2/' protobufs/*.proto 60 | sed -i 's/cc_generic_services/py_generic_services/' protobufs/*.proto 61 | sed -i 's/ = 6000/ = 7000/' protobufs/steammessages.proto # extentions hack 62 | 63 | pb_compile: 64 | for filepath in `ls ./protobufs/*.proto`; do \ 65 | protoc3 --python_out ./dota2/protobufs/ --proto_path=./protobufs "$$filepath"; \ 66 | done; 67 | sed -i '/^import sys/! s/^import /import dota2.protobufs./' dota2/protobufs/*_pb2.py 68 | 69 | pb_clear: 70 | rm -f ./protobufs/*.proto ./dota2/protobufs/*_pb2.py 71 | 72 | gen_enums: 73 | python gen_enum_from_protos.py > dota2/proto_enums.py 74 | 75 | pb_update: pb_fetch pb_compile gen_enums 76 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | | |pypi| |license| |docs| 2 | | |sonar_maintainability| |sonar_reliability| |sonar_security| 3 | 4 | Supports Python ``2.7+`` and ``3.4+``. 5 | 6 | Module based on `steam `_ 7 | for interacting with Dota2's Game Coordinator. If you've used 8 | `node-dota2 `_ then 9 | this module should feel right at home. Check out the documentation 10 | to get started. 11 | 12 | **Documentation**: http://dota2.readthedocs.io 13 | 14 | Contributions and suggestion are always welcome. 15 | 16 | 17 | Installation 18 | ------------ 19 | 20 | Install latest version from PYPI:: 21 | 22 | pip install dota2 23 | 24 | Install the current dev version from ``github``:: 25 | 26 | pip install git+https://github.com/ValvePython/dota2 27 | 28 | # if you are installing over existing install 29 | # note: "only-if-needed" will only update dependecies if needed 30 | pip install -U --upgrade-strategy only-if-needed git+https://github.com/ValvePython/dota2 31 | 32 | 33 | 34 | .. |pypi| image:: https://img.shields.io/pypi/v/dota2.svg?style=flat&label=latest%20version 35 | :target: https://pypi.python.org/pypi/dota2 36 | :alt: Latest version released on PyPi 37 | 38 | .. |license| image:: https://img.shields.io/pypi/l/dota2.svg?style=flat&label=license 39 | :target: https://pypi.python.org/pypi/dota2 40 | :alt: MIT License 41 | 42 | .. |docs| image:: https://readthedocs.org/projects/dota2/badge/?version=latest 43 | :target: http://dota2.readthedocs.io/en/latest/?badge=latest 44 | :alt: Documentation status 45 | 46 | .. |sonar_maintainability| image:: https://sonarcloud.io/api/project_badges/measure?project=ValvePython_dota2&metric=sqale_rating 47 | :target: https://sonarcloud.io/dashboard?id=ValvePython_dota2 48 | :alt: SonarCloud Rating 49 | 50 | .. |sonar_reliability| image:: https://sonarcloud.io/api/project_badges/measure?project=ValvePython_dota2&metric=reliability_rating 51 | :target: https://sonarcloud.io/dashboard?id=ValvePython_dota2 52 | :alt: SonarCloud Rating 53 | 54 | .. |sonar_security| image:: https://sonarcloud.io/api/project_badges/measure?project=ValvePython_dota2&metric=security_rating 55 | :target: https://sonarcloud.io/dashboard?id=ValvePython_dota2 56 | :alt: SonarCloud Rating 57 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _doc 2 | _build 3 | _static 4 | _templates 5 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | .PHONY: html 55 | html: 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | .PHONY: dirhtml 61 | dirhtml: 62 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 63 | @echo 64 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 65 | 66 | .PHONY: singlehtml 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | .PHONY: pickle 73 | pickle: 74 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 75 | @echo 76 | @echo "Build finished; now you can process the pickle files." 77 | 78 | .PHONY: json 79 | json: 80 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 81 | @echo 82 | @echo "Build finished; now you can process the JSON files." 83 | 84 | .PHONY: htmlhelp 85 | htmlhelp: 86 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 87 | @echo 88 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 89 | ".hhp project file in $(BUILDDIR)/htmlhelp." 90 | 91 | .PHONY: qthelp 92 | qthelp: 93 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 94 | @echo 95 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 96 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 97 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/dota2.qhcp" 98 | @echo "To view the help file:" 99 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/dota2.qhc" 100 | 101 | .PHONY: applehelp 102 | applehelp: 103 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 104 | @echo 105 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 106 | @echo "N.B. You won't be able to view it unless you put it in" \ 107 | "~/Library/Documentation/Help or install it in your application" \ 108 | "bundle." 109 | 110 | .PHONY: devhelp 111 | devhelp: 112 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 113 | @echo 114 | @echo "Build finished." 115 | @echo "To view the help file:" 116 | @echo "# mkdir -p $$HOME/.local/share/devhelp/dota2" 117 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/dota2" 118 | @echo "# devhelp" 119 | 120 | .PHONY: epub 121 | epub: 122 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 123 | @echo 124 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 125 | 126 | .PHONY: latex 127 | latex: 128 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 129 | @echo 130 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 131 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 132 | "(use \`make latexpdf' here to do that automatically)." 133 | 134 | .PHONY: latexpdf 135 | latexpdf: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through pdflatex..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | .PHONY: latexpdfja 142 | latexpdfja: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through platex and dvipdfmx..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: text 149 | text: 150 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 151 | @echo 152 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 153 | 154 | .PHONY: man 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | .PHONY: texinfo 161 | texinfo: 162 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 163 | @echo 164 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 165 | @echo "Run \`make' in that directory to run these through makeinfo" \ 166 | "(use \`make info' here to do that automatically)." 167 | 168 | .PHONY: info 169 | info: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo "Running Texinfo files through makeinfo..." 172 | make -C $(BUILDDIR)/texinfo info 173 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 174 | 175 | .PHONY: gettext 176 | gettext: 177 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 178 | @echo 179 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 180 | 181 | .PHONY: changes 182 | changes: 183 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 184 | @echo 185 | @echo "The overview file is in $(BUILDDIR)/changes." 186 | 187 | .PHONY: linkcheck 188 | linkcheck: 189 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 190 | @echo 191 | @echo "Link check complete; look for any errors in the above output " \ 192 | "or in $(BUILDDIR)/linkcheck/output.txt." 193 | 194 | .PHONY: doctest 195 | doctest: 196 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 197 | @echo "Testing of doctests in the sources finished, look at the " \ 198 | "results in $(BUILDDIR)/doctest/output.txt." 199 | 200 | .PHONY: coverage 201 | coverage: 202 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 203 | @echo "Testing of coverage in the sources finished, look at the " \ 204 | "results in $(BUILDDIR)/coverage/python.txt." 205 | 206 | .PHONY: xml 207 | xml: 208 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 209 | @echo 210 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 211 | 212 | .PHONY: pseudoxml 213 | pseudoxml: 214 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 215 | @echo 216 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 217 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # dota2 documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Feb 15 03:43:47 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | sys.path.insert(0, os.path.abspath('../')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.intersphinx', 34 | # 'sphinx.ext.githubpages', 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # The suffix(es) of source filenames. 41 | # You can specify multiple suffix as a list of string: 42 | # source_suffix = ['.rst', '.md'] 43 | source_suffix = '.rst' 44 | 45 | # The encoding of source files. 46 | #source_encoding = 'utf-8-sig' 47 | 48 | # The master toctree document. 49 | master_doc = 'index' 50 | 51 | # General information about the project. 52 | from dota2 import __version__, __author__ 53 | project = u'dota2' 54 | copyright = u'2019, %s' % __author__ 55 | author = __author__ 56 | 57 | # The version info for the project you're documenting, acts as replacement for 58 | # |version| and |release|, also used in various other places throughout the 59 | # built documents. 60 | # The short X.Y version. 61 | version = __version__ 62 | # The full version, including alpha/beta/rc tags. 63 | release = __version__ 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = 'en' 71 | 72 | # There are two options for replacing |today|: either, you set today to some 73 | # non-false value, then it is used: 74 | #today = '' 75 | # Else, today_fmt is used as the format for a strftime call. 76 | #today_fmt = '%B %d, %Y' 77 | 78 | # List of patterns, relative to source directory, that match files and 79 | # directories to ignore when looking for source files. 80 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 81 | 82 | # The reST default role (used for this markup: `text`) to use for all 83 | # documents. 84 | #default_role = None 85 | 86 | # If true, '()' will be appended to :func: etc. cross-reference text. 87 | #add_function_parentheses = True 88 | 89 | # If true, the current module name will be prepended to all description 90 | # unit titles (such as .. function::). 91 | #add_module_names = True 92 | 93 | # If true, sectionauthor and moduleauthor directives will be shown in the 94 | # output. They are ignored by default. 95 | #show_authors = False 96 | 97 | # The name of the Pygments (syntax highlighting) style to use. 98 | pygments_style = 'sphinx' 99 | 100 | # A list of ignored prefixes for module index sorting. 101 | #modindex_common_prefix = [] 102 | 103 | # If true, keep warnings as "system message" paragraphs in the built documents. 104 | #keep_warnings = False 105 | 106 | # If true, `todo` and `todoList` produce output, else they produce nothing. 107 | todo_include_todos = True 108 | 109 | 110 | # -- Options for HTML output ---------------------------------------------- 111 | 112 | # The theme to use for HTML and HTML Help pages. See the documentation for 113 | # a list of builtin themes. 114 | html_theme = 'sphinx_rtd_theme' 115 | 116 | # Theme options are theme-specific and customize the look and feel of a theme 117 | # further. For a list of options available for each theme, see the 118 | # documentation. 119 | #html_theme_options = {} 120 | 121 | # Add any paths that contain custom themes here, relative to this directory. 122 | #html_theme_path = [] 123 | 124 | # The name for this set of Sphinx documents. 125 | # " v documentation" by default. 126 | #html_title = u'dota2 v' 127 | 128 | # A shorter title for the navigation bar. Default is the same as html_title. 129 | #html_short_title = None 130 | 131 | # The name of an image file (relative to this directory) to place at the top 132 | # of the sidebar. 133 | #html_logo = None 134 | 135 | # The name of an image file (within the static path) to use as favicon of the 136 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 137 | # pixels large. 138 | #html_favicon = None 139 | 140 | # Add any paths that contain custom static files (such as style sheets) here, 141 | # relative to this directory. They are copied after the builtin static files, 142 | # so a file named "default.css" will overwrite the builtin "default.css". 143 | html_static_path = ['_static'] 144 | 145 | # Add any extra paths that contain custom files (such as robots.txt or 146 | # .htaccess) here, relative to this directory. These files are copied 147 | # directly to the root of the documentation. 148 | #html_extra_path = [] 149 | 150 | # If not None, a 'Last updated on:' timestamp is inserted at every page 151 | # bottom, using the given strftime format. 152 | # The empty string is equivalent to '%b %d, %Y'. 153 | #html_last_updated_fmt = None 154 | 155 | # If true, SmartyPants will be used to convert quotes and dashes to 156 | # typographically correct entities. 157 | #html_use_smartypants = True 158 | 159 | # Custom sidebar templates, maps document names to template names. 160 | #html_sidebars = {} 161 | 162 | # Additional templates that should be rendered to pages, maps page names to 163 | # template names. 164 | #html_additional_pages = {} 165 | 166 | # If false, no module index is generated. 167 | #html_domain_indices = True 168 | 169 | # If false, no index is generated. 170 | #html_use_index = True 171 | 172 | # If true, the index is split into individual pages for each letter. 173 | #html_split_index = False 174 | 175 | # If true, links to the reST sources are added to the pages. 176 | html_show_sourcelink = True 177 | 178 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 179 | html_show_sphinx = True 180 | 181 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 182 | #html_show_copyright = True 183 | 184 | # If true, an OpenSearch description file will be output, and all pages will 185 | # contain a tag referring to it. The value of this option must be the 186 | # base URL from which the finished HTML is served. 187 | #html_use_opensearch = '' 188 | 189 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 190 | #html_file_suffix = None 191 | 192 | # Language to be used for generating the HTML full-text search index. 193 | # Sphinx supports the following languages: 194 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 195 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' 196 | #html_search_language = 'en' 197 | 198 | # A dictionary with options for the search language support, empty by default. 199 | # 'ja' uses this config value. 200 | # 'zh' user can custom change `jieba` dictionary path. 201 | #html_search_options = {'type': 'default'} 202 | 203 | # The name of a javascript file (relative to the configuration directory) that 204 | # implements a search results scorer. If empty, the default will be used. 205 | #html_search_scorer = 'scorer.js' 206 | 207 | # Output file base name for HTML help builder. 208 | htmlhelp_basename = 'dota2doc' 209 | 210 | # -- Options for LaTeX output --------------------------------------------- 211 | 212 | latex_elements = { 213 | # The paper size ('letterpaper' or 'a4paper'). 214 | #'papersize': 'letterpaper', 215 | 216 | # The font size ('10pt', '11pt' or '12pt'). 217 | #'pointsize': '10pt', 218 | 219 | # Additional stuff for the LaTeX preamble. 220 | #'preamble': '', 221 | 222 | # Latex figure (float) alignment 223 | #'figure_align': 'htbp', 224 | } 225 | 226 | # Grouping the document tree into LaTeX files. List of tuples 227 | # (source start file, target name, title, 228 | # author, documentclass [howto, manual, or own class]). 229 | latex_documents = [ 230 | (master_doc, 'dota2.tex', u'dota2 Documentation', 231 | version, 'manual'), 232 | ] 233 | 234 | # The name of an image file (relative to this directory) to place at the top of 235 | # the title page. 236 | #latex_logo = None 237 | 238 | # For "manual" documents, if this is true, then toplevel headings are parts, 239 | # not chapters. 240 | #latex_use_parts = False 241 | 242 | # If true, show page references after internal links. 243 | #latex_show_pagerefs = False 244 | 245 | # If true, show URL addresses after external links. 246 | #latex_show_urls = False 247 | 248 | # Documents to append as an appendix to all manuals. 249 | #latex_appendices = [] 250 | 251 | # If false, no module index is generated. 252 | #latex_domain_indices = True 253 | 254 | 255 | # -- Options for manual page output --------------------------------------- 256 | 257 | # One entry per manual page. List of tuples 258 | # (source start file, name, description, authors, manual section). 259 | man_pages = [ 260 | (master_doc, 'dota2', u'dota2 Documentation', 261 | [author], 1) 262 | ] 263 | 264 | # If true, show URL addresses after external links. 265 | #man_show_urls = False 266 | 267 | 268 | # -- Options for Texinfo output ------------------------------------------- 269 | 270 | # Grouping the document tree into Texinfo files. List of tuples 271 | # (source start file, target name, title, author, 272 | # dir menu entry, description, category) 273 | texinfo_documents = [ 274 | (master_doc, 'dota2', u'dota2 Documentation', 275 | author, 'dota2', 'One line description of project.', 276 | 'Miscellaneous'), 277 | ] 278 | 279 | # Documents to append as an appendix to all manuals. 280 | #texinfo_appendices = [] 281 | 282 | # If false, no module index is generated. 283 | #texinfo_domain_indices = True 284 | 285 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 286 | #texinfo_show_urls = 'footnote' 287 | 288 | # If true, do not generate a @detailmenu in the "Top" node's menu. 289 | #texinfo_no_detailmenu = False 290 | 291 | 292 | # -- Options for Epub output ---------------------------------------------- 293 | 294 | # Bibliographic Dublin Core info. 295 | epub_title = project 296 | epub_author = author 297 | epub_publisher = author 298 | epub_copyright = copyright 299 | 300 | # The basename for the epub file. It defaults to the project name. 301 | #epub_basename = project 302 | 303 | # The HTML theme for the epub output. Since the default themes are not 304 | # optimized for small screen space, using the same theme for HTML and epub 305 | # output is usually not wise. This defaults to 'epub', a theme designed to save 306 | # visual space. 307 | #epub_theme = 'epub' 308 | 309 | # The language of the text. It defaults to the language option 310 | # or 'en' if the language is not set. 311 | #epub_language = '' 312 | 313 | # The scheme of the identifier. Typical schemes are ISBN or URL. 314 | #epub_scheme = '' 315 | 316 | # The unique identifier of the text. This can be a ISBN number 317 | # or the project homepage. 318 | #epub_identifier = '' 319 | 320 | # A unique identification for the text. 321 | #epub_uid = '' 322 | 323 | # A tuple containing the cover image and cover page html template filenames. 324 | #epub_cover = () 325 | 326 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 327 | #epub_guide = () 328 | 329 | # HTML files that should be inserted before the pages created by sphinx. 330 | # The format is a list of tuples containing the path and title. 331 | #epub_pre_files = [] 332 | 333 | # HTML files shat should be inserted after the pages created by sphinx. 334 | # The format is a list of tuples containing the path and title. 335 | #epub_post_files = [] 336 | 337 | # A list of files that should not be packed into the epub file. 338 | epub_exclude_files = ['search.html'] 339 | 340 | # The depth of the table of contents in toc.ncx. 341 | #epub_tocdepth = 3 342 | 343 | # Allow duplicate toc entries. 344 | #epub_tocdup = True 345 | 346 | # Choose between 'default' and 'includehidden'. 347 | #epub_tocscope = 'default' 348 | 349 | # Fix unsupported image types using the Pillow. 350 | #epub_fix_images = False 351 | 352 | # Scale large images. 353 | #epub_max_image_width = 0 354 | 355 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 356 | #epub_show_urls = 'inline' 357 | 358 | # If false, no index is generated. 359 | #epub_use_index = True 360 | 361 | # LINK EXT DOCS 362 | intersphinx_mapping = { 363 | 'python': ('https://docs.python.org/3.6', None), 364 | 'gevent': ('http://www.gevent.org', None), 365 | 'requests': ('https://2.python-requests.org/en/master', None), 366 | 'steam': ('https://steam.readthedocs.io/en/stable/', None), 367 | } 368 | 369 | # AUTODOC 370 | autodoc_member_order = 'bysource' 371 | -------------------------------------------------------------------------------- /docs/dota2.client.rst: -------------------------------------------------------------------------------- 1 | client 2 | ====== 3 | 4 | .. automodule:: dota2.client 5 | :members: 6 | :show-inheritance: 7 | -------------------------------------------------------------------------------- /docs/dota2.enums.rst: -------------------------------------------------------------------------------- 1 | enums 2 | ===== 3 | 4 | .. automodule:: dota2.common_enums 5 | :members: 6 | :undoc-members: 7 | :inherited-members: 8 | 9 | .. automodule:: dota2.proto_enums 10 | :members: 11 | :undoc-members: 12 | :inherited-members: 13 | -------------------------------------------------------------------------------- /docs/dota2.features.chat.rst: -------------------------------------------------------------------------------- 1 | chat 2 | ==== 3 | 4 | Chat channel features 5 | 6 | 7 | .. automodule:: dota2.features.chat 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/dota2.features.lobby.rst: -------------------------------------------------------------------------------- 1 | lobby 2 | ===== 3 | 4 | Lobby related features 5 | 6 | 7 | .. automodule:: dota2.features.lobby 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/dota2.features.match.rst: -------------------------------------------------------------------------------- 1 | match 2 | ===== 3 | 4 | Features related to matches and matchmaking. 5 | 6 | .. automodule:: dota2.features.match 7 | :members: 8 | :undoc-members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/dota2.features.party.rst: -------------------------------------------------------------------------------- 1 | party 2 | ====== 3 | 4 | Features related to party invite and communication. 5 | 6 | .. automodule:: dota2.features.party 7 | :members: 8 | :undoc-members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/dota2.features.player.rst: -------------------------------------------------------------------------------- 1 | player 2 | ====== 3 | 4 | Features related to community, players and profiles. 5 | 6 | .. automodule:: dota2.features.player 7 | :members: 8 | :undoc-members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/dota2.features.rst: -------------------------------------------------------------------------------- 1 | features 2 | ======== 3 | 4 | This package contains all high level features of the :class:`dota2.client.Dota2Client`. 5 | 6 | .. toctree:: 7 | 8 | dota2.features.player 9 | dota2.features.match 10 | dota2.features.party 11 | dota2.features.lobby 12 | dota2.features.chat 13 | dota2.features.sharedobjects 14 | -------------------------------------------------------------------------------- /docs/dota2.features.sharedobjects.rst: -------------------------------------------------------------------------------- 1 | sharedobjects 2 | ============= 3 | 4 | .. automodule:: dota2.features.sharedobjects 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/dota2.msg.rst: -------------------------------------------------------------------------------- 1 | msg 2 | === 3 | 4 | .. automodule:: dota2.msg 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/dota2.rst: -------------------------------------------------------------------------------- 1 | dota2 API 2 | ========= 3 | 4 | Documentation related to various APIs available in this package. 5 | 6 | .. toctree:: 7 | 8 | dota2.features 9 | dota2.client 10 | dota2.enums 11 | dota2.msg 12 | dota2.utils 13 | -------------------------------------------------------------------------------- /docs/dota2.utils.rst: -------------------------------------------------------------------------------- 1 | utils 2 | ===== 3 | 4 | .. automodule:: dota2.utils 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to dota2's documentation! 2 | ================================= 3 | 4 | |pypi| |license| 5 | 6 | Supports Python ``2.7+`` and ``3.4+``. 7 | 8 | | Module based on `steam `_ for interacting with Dota 2's Game Coordinator. 9 | | If you've used `node-dota2 `_ this module should feel familiar. 10 | 11 | As always contributions and suggestions are welcome. 12 | Just visit the `repository on github `_. 13 | 14 | Getting started 15 | --------------- 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | 20 | user_guide 21 | 22 | API Documentation 23 | ----------------- 24 | 25 | .. toctree:: 26 | :maxdepth: 4 27 | 28 | dota2 29 | 30 | 31 | Indices and tables 32 | ================== 33 | 34 | * :ref:`genindex` 35 | * :ref:`modindex` 36 | * :ref:`search` 37 | 38 | .. |pypi| image:: https://img.shields.io/pypi/v/dota2.svg?style=flat&label=latest%20version 39 | :target: https://pypi.python.org/pypi/dota2 40 | :alt: Latest version released on PyPi 41 | 42 | .. |license| image:: https://img.shields.io/pypi/l/dota2.svg?style=flat&label=license 43 | :target: https://pypi.python.org/pypi/dota2 44 | :alt: MIT License 45 | -------------------------------------------------------------------------------- /docs/user_guide.rst: -------------------------------------------------------------------------------- 1 | User Guide 2 | ********** 3 | 4 | This part of the documentation is a quick start for writing applications that 5 | interact with the game coordinator for Dota 2. 6 | 7 | 8 | Initialization 9 | ============== 10 | 11 | Below is a example how to login and get a session with game coordinator. 12 | See `steam's docs `_ 13 | for details about :class:`SteamClient `. 14 | 15 | .. note:: 16 | You won't see any output running the code above. 17 | In order to peek inside we need to setup debug logging. 18 | See the :ref:`logging_config` section 19 | 20 | 21 | .. code:: python 22 | 23 | from steam.client import SteamClient 24 | from dota2.client import Dota2Client 25 | 26 | client = SteamClient() 27 | dota = Dota2Client(client) 28 | 29 | @client.on('logged_on') 30 | def start_dota(): 31 | dota.launch() 32 | 33 | @dota.on('ready') 34 | def do_dota_stuff(): 35 | # talk to GC 36 | 37 | client.cli_login() 38 | client.run_forever() 39 | 40 | 41 | Working with events 42 | =================== 43 | 44 | This module makes use of `gevent `_ 45 | and `gevent-eventemitter `_. 46 | Working with events is similiar to ``EventEmitter`` in javascript. 47 | Nevertheless, here is quick rundown. 48 | 49 | To catch an event we need to register a callback 50 | 51 | .. code:: python 52 | 53 | @dota.on('my event') 54 | def do_stuff(a, b): 55 | print "Hey!" 56 | 57 | dota.on('my event', do_stuff) 58 | dota.once('my event', do_stuff) # call do_stuff just one time 59 | dota.wait_event('my event') # blocks and returns arguments, if any 60 | 61 | .. note:: 62 | ``wait_event`` may block forever, so use the ``timeout`` parameter 63 | 64 | Emitting an event is simple 65 | 66 | .. code:: python 67 | 68 | dota.emit("my event") 69 | dota.emit("my event", 1, [3,4,5]) # optional arguments 70 | 71 | 72 | That's it. For more details see `gevent-eventemitter `_. 73 | 74 | 75 | Fetch player profile card 76 | ========================= 77 | 78 | You've probably seen the profile cards in Dota 2. 79 | They contain player selected stats, such trophies, number of matches, or MMR. 80 | 81 | We can request that data using an API from the :doc:`dota2.features` module. 82 | 83 | Let's get Dendi's profile card. All we need is his account id, which is ``70388657``. 84 | 85 | .. code:: python 86 | 87 | @dota.on('ready') 88 | def fetch_profile_card(): 89 | dota.request_profile_card(70388657) 90 | 91 | @dota.on('profile_card'): 92 | def print_profile_card(account_id, profile_card): 93 | if account_id == 70388657: 94 | print str(profile_card) 95 | 96 | The profile card request also happens to be a job. 97 | ``request_profile_card`` returns a ``job id`` and we can wait for it instead. 98 | However, we will not get the same parameters as from ``profile_card`` 99 | 100 | .. note:: 101 | Listening for the ``job id``` will only give you one arugment: the protobuf message 102 | 103 | .. code:: python 104 | 105 | @dota.on('ready') 106 | def fetch_profile_card(): 107 | jobid = dota.request_profile_card(70388657) 108 | profile_card = dota.wait_msg(jobid, timeout=10) 109 | 110 | if profile_card: 111 | print str(profile_card) 112 | 113 | .. note:: 114 | Not every request returns a ``job id``, see the API documentation for details 115 | 116 | Running the code above will output something like this: 117 | 118 | .. code:: 119 | 120 | account_id: 70388657 121 | background_def_index: 0 122 | slots { 123 | slot_id: 0 124 | stat { 125 | stat_id: k_eStat_FirstMatchDate 126 | stat_score: 1314309005 127 | } 128 | } 129 | slots { 130 | slot_id: 1 131 | stat { 132 | stat_id: k_eStat_SoloRank 133 | stat_score: 6775 134 | 135 | 136 | .. _logging_config: 137 | 138 | Configure console logging 139 | ========================= 140 | 141 | Here is a basic configuration to get debug messages in the console. 142 | 143 | .. code:: python 144 | 145 | import logging 146 | 147 | logging.basicConfig(format='[%(asctime)s] %(levelname)s %(name)s: %(message)s', level=logging.DEBUG) 148 | 149 | The we run the program and the console ouput should look something like this: 150 | 151 | .. code:: 152 | 153 | [2016-01-01 12:34:56,000] DEBUG CMClient: Connect initiated. 154 | [2016-01-01 12:34:56,000] DEBUG Connection: Attempting connection to ('208.78.164.13', 27018) 155 | [2016-01-01 12:34:56,000] DEBUG Connection: Connected. 156 | [2016-01-01 12:34:56,000] DEBUG CMClient: Emit event: 'connected' 157 | [2016-01-01 12:34:56,000] DEBUG SteamClient: Emit event: 'connected' 158 | [2016-01-01 12:34:56,000] DEBUG SteamClient: Attempting login 159 | [2016-01-01 12:34:56,000] DEBUG CMClient: Incoming: > 160 | [2016-01-01 12:34:56,000] DEBUG CMClient: Emit event: 161 | ... 162 | 163 | 164 | -------------------------------------------------------------------------------- /dota2/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.1.0" 2 | __author__ = "Rossen Georgiev" 3 | 4 | version_info = (1, 1, 0) 5 | -------------------------------------------------------------------------------- /dota2/client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Only the most essential features to :class:`dota2.client.Dota2Client` are found here. Every other feature is inherited from 3 | the :mod:`dota2.features` package and it's submodules. 4 | """ 5 | 6 | import logging 7 | import gevent 8 | import google.protobuf 9 | from eventemitter import EventEmitter 10 | from steam.core.msg import GCMsgHdrProto 11 | from steam.client.gc import GameCoordinator 12 | from steam.enums.emsg import EMsg 13 | from steam.utils.proto import proto_fill_from_dict 14 | from dota2.features import FeatureBase 15 | from dota2.enums import EGCBaseClientMsg, EDOTAGCSessionNeed, GCConnectionStatus, ESourceEngine 16 | from dota2.msg import get_emsg_enum, find_proto 17 | from dota2.protobufs import gcsdk_gcmessages_pb2 as pb_gc 18 | from dota2.protobufs import dota_gcmessages_client_pb2 as pb_gclient 19 | 20 | 21 | 22 | class Dota2Client(GameCoordinator, FeatureBase): 23 | """ 24 | :param steam_client: Instance of the steam client 25 | :type steam_client: :class:`steam.client.SteamClient` 26 | """ 27 | _retry_welcome_loop = None 28 | verbose_debug = False #: enable pretty print of messages in debug logging 29 | app_id = 570 #: main client app id 30 | current_jobid = 0 31 | ready = False #: ``True`` when we have a session with GC 32 | connection_status = GCConnectionStatus.NO_SESSION #: :class:`dota2.enums.GCConnectionStatus` 33 | 34 | @property 35 | def account_id(self): 36 | """ 37 | Account ID of the logged in user in the steam client 38 | """ 39 | return self.steam.steam_id.id 40 | 41 | @property 42 | def steam_id(self): 43 | """ 44 | :class:`steam.steamid.SteamID` of the logged-in user in the steam client 45 | """ 46 | return self.steam.steam_id 47 | 48 | def __init__(self, steam_client): 49 | GameCoordinator.__init__(self, steam_client, self.app_id) 50 | self._LOG = logging.getLogger(self.__class__.__name__) 51 | 52 | FeatureBase.__init__(self) 53 | 54 | self.steam.on('disconnected', self._handle_disconnect) 55 | self.steam.on(EMsg.ClientPlayingSessionState, self._handle_play_sess_state) 56 | 57 | # register GC message handles 58 | self.on(EGCBaseClientMsg.EMsgGCClientConnectionStatus, self._handle_conn_status) 59 | self.on(EGCBaseClientMsg.EMsgGCClientWelcome, self._handle_client_welcome) 60 | 61 | def __repr__(self): 62 | return "<%s(%s) %s>" % (self.__class__.__name__, 63 | repr(self.steam), 64 | repr(self.connection_status), 65 | ) 66 | 67 | def _handle_play_sess_state(self, message): 68 | if self.ready and message.body.playing_app != self.app_id: 69 | self._set_connection_status(GCConnectionStatus.NO_SESSION) 70 | 71 | def _handle_disconnect(self): 72 | if self._retry_welcome_loop: 73 | self._retry_welcome_loop.kill() 74 | 75 | self._set_connection_status(GCConnectionStatus.NO_SESSION) 76 | 77 | def _handle_client_welcome(self, message): 78 | self._set_connection_status(GCConnectionStatus.HAVE_SESSION) 79 | 80 | # handle DOTAWelcome 81 | submessage = pb_gclient.CMsgDOTAWelcome() 82 | submessage.ParseFromString(message.game_data) 83 | 84 | if self.verbose_debug: 85 | self._LOG.debug("Got DOTAWelcome:\n%s" % str(submessage)) 86 | else: 87 | self._LOG.debug("Got DOTAWelcome") 88 | 89 | self.emit('dota_welcome', submessage) 90 | 91 | for extra in submessage.extra_messages: 92 | self._process_gc_message(extra.id, GCMsgHdrProto(extra.id), extra.contents) 93 | 94 | 95 | def _handle_conn_status(self, message): 96 | self._set_connection_status(message.status) 97 | 98 | def _process_gc_message(self, emsg, header, payload): 99 | emsg = get_emsg_enum(emsg) 100 | proto = find_proto(emsg) 101 | 102 | if proto is None: 103 | self._LOG.error("Failed to parse: %s" % repr(emsg)) 104 | return 105 | 106 | message = proto() 107 | message.ParseFromString(payload) 108 | 109 | if self.verbose_debug: 110 | self._LOG.debug("Incoming: %s\n%s\n---------\n%s" % (repr(emsg), 111 | str(header), 112 | str(message), 113 | )) 114 | else: 115 | self._LOG.debug("Incoming: %s", repr(emsg)) 116 | 117 | self.emit(emsg, message) 118 | 119 | if header.proto.job_id_target != 18446744073709551615: 120 | self.emit('job_%d' % header.proto.job_id_target, message) 121 | 122 | def _set_connection_status(self, status): 123 | prev_status = self.connection_status 124 | self.connection_status = GCConnectionStatus(status) 125 | 126 | if self.connection_status != prev_status: 127 | self.emit("connection_status", self.connection_status) 128 | 129 | if self.connection_status == GCConnectionStatus.HAVE_SESSION and not self.ready: 130 | self.ready = True 131 | self.emit('ready') 132 | elif self.connection_status != GCConnectionStatus.HAVE_SESSION and self.ready: 133 | self.ready = False 134 | self.emit('notready') 135 | 136 | def wait_msg(self, event, timeout=None, raises=None): 137 | """Wait for a message, similiar to :meth:`.wait_event` 138 | 139 | :param event: :class:`.EDOTAGCMsg` or job id 140 | :param timeout: seconds to wait before timeout 141 | :type timeout: :class:`int` 142 | :param raises: On timeout when ``False`` returns :class:`None`, else raise :class:`gevent.Timeout` 143 | :type raises: :class:`bool` 144 | :return: returns a message or :class:`None` 145 | :rtype: :class:`None`, or `proto message` 146 | :raises: ``gevent.Timeout` 147 | """ 148 | resp = self.wait_event(event, timeout, raises) 149 | 150 | if resp is not None: 151 | return resp[0] 152 | 153 | def send_job(self, *args, **kwargs): 154 | """ 155 | Send a message as a job 156 | 157 | Exactly the same as :meth:`send` 158 | 159 | :return: jobid event identifier 160 | :rtype: :class:`str` 161 | 162 | """ 163 | jobid = self.current_jobid = ((self.current_jobid + 1) % 10000) or 1 164 | self.remove_all_listeners('job_%d' % jobid) 165 | 166 | self._send(*args, jobid=jobid, **kwargs) 167 | 168 | return "job_%d" % jobid 169 | 170 | def send_job_and_wait(self, emsg, data={}, proto=None, timeout=None, raises=False): 171 | """ 172 | Send a message as a job and wait for the response. 173 | 174 | .. note:: 175 | Not all messages are jobs, you'll have to find out which are which 176 | 177 | :param emsg: Enum for the message 178 | :param data: data for the proto message 179 | :type data: :class:`dict` 180 | :param proto: (optional) specify protobuf, otherwise it's detected based on ``emsg`` 181 | :param timeout: (optional) seconds to wait 182 | :type timeout: :class:`int` 183 | :param raises: (optional) On timeout if this is ``False`` method will return ``None``, else raises ``gevent.Timeout`` 184 | :type raises: :class:`bool` 185 | :return: response proto message 186 | :raises: :class:`gevent.Timeout`` 187 | """ 188 | job_id = self.send_job(emsg, data, proto) 189 | return self.wait_msg(job_id, timeout, raises=raises) 190 | 191 | def send(self, emsg, data={}, proto=None): 192 | """ 193 | Send a message 194 | 195 | :param emsg: Enum for the message 196 | :param data: data for the proto message 197 | :type data: :class:`dict` 198 | :param proto: (optional) manually specify protobuf, other it's detected based on ``emsg`` 199 | """ 200 | self._send(emsg, data, proto) 201 | 202 | def _send(self, emsg, data={}, proto=None, jobid=None): 203 | if not isinstance(data, dict): 204 | raise ValueError("data kwarg can only be a dict") 205 | 206 | if proto is None: 207 | proto = find_proto(emsg) 208 | 209 | if not issubclass(proto, google.protobuf.message.Message): 210 | raise ValueError("Unable to find proto for emsg, or proto kwarg is invalid") 211 | 212 | message = proto() 213 | proto_fill_from_dict(message, data) 214 | 215 | header = GCMsgHdrProto(emsg) 216 | 217 | if jobid is not None: 218 | header.proto.job_id_source = jobid 219 | 220 | if self.verbose_debug: 221 | str_message = '' 222 | str_header = str(header) 223 | str_body = str(message) 224 | 225 | if str_header: 226 | str_message += "-- header ---------\n%s\n" % str_header 227 | if str_body: 228 | str_message += "-- message --------\n%s\n" % str_body 229 | 230 | self._LOG.debug("Outgoing: %s\n%s" % (repr(emsg), str_message)) 231 | else: 232 | self._LOG.debug("Outgoing: %s", repr(emsg)) 233 | 234 | GameCoordinator.send(self, header, message.SerializeToString()) 235 | 236 | def _knock_on_gc(self): 237 | n = 1 238 | 239 | while True: 240 | if not self.ready: 241 | self.send(EGCBaseClientMsg.EMsgGCClientHello, { 242 | 'client_session_need': EDOTAGCSessionNeed.UserInUINeverConnected, 243 | 'engine': ESourceEngine.ESE_Source2, 244 | }) 245 | 246 | self.wait_event('ready', timeout=3 + (2**n)) 247 | n = min(n + 1, 4) 248 | 249 | else: 250 | self.wait_event('notready') 251 | n = 1 252 | gevent.sleep(1) 253 | 254 | def launch(self): 255 | """ 256 | Launch Dota 2 and establish connection with the game coordinator 257 | 258 | ``ready`` event will fire when the session is ready. 259 | If the session is lost ``notready`` event will fire. 260 | Alternatively, ``connection_status`` event can be monitored for changes. 261 | """ 262 | if not self.steam.logged_on: 263 | return 264 | 265 | if not self._retry_welcome_loop and self.app_id not in self.steam.current_games_played: 266 | self.steam.games_played(self.steam.current_games_played + [self.app_id]) 267 | self._retry_welcome_loop = gevent.spawn(self._knock_on_gc) 268 | 269 | def exit(self): 270 | """ 271 | Close connection to Dota 2's game coordinator 272 | """ 273 | if self._retry_welcome_loop: 274 | self._retry_welcome_loop.kill() 275 | 276 | if self.app_id in self.steam.current_games_played: 277 | self.steam.current_games_played.remove(self.app_id) 278 | self.steam.games_played(self.steam.current_games_played) 279 | 280 | self._set_connection_status(GCConnectionStatus.NO_SESSION) 281 | 282 | def sleep(self, seconds): 283 | """Yeild and sleep N seconds. Allows other greenlets to run""" 284 | gevent.sleep(seconds) 285 | 286 | def idle(self): 287 | """Yeild in the current greenlet and let other greenlets run""" 288 | gevent.idle() 289 | -------------------------------------------------------------------------------- /dota2/common_enums.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import IntEnum 3 | 4 | 5 | class ESOType(IntEnum): 6 | CSOEconItem = 1 7 | CSOItemRecipe = 5 8 | CSOEconGameAccountClient = 7 9 | CSOSelectedItemPreset = 35 # no longer exists in game files 10 | CSOEconItemPresetInstance = 36 # no longer exists in game files 11 | CSOEconItemDropRateBonus = 38 12 | CSOEconItemLeagueViewPass = 39 13 | CSOEconItemEventTicket = 40 14 | CSOEconItemTournamentPassport = 42 15 | CSODOTAGameAccountClient = 2002 16 | CSODOTAParty = 2003 17 | CSODOTALobby = 2004 18 | CSODOTAPartyInvite = 2006 19 | CSODOTAGameHeroFavorites = 2007 20 | CSODOTAMapLocationState = 2008 21 | CMsgDOTATournament = 2009 22 | CSODOTAPlayerChallenge = 2010 23 | CSODOTALobbyInvite = 2011 24 | CSODOTAGameAccountPlus = 2012 25 | 26 | 27 | class EServerRegion(IntEnum): 28 | Unspecified = 0 29 | USWest = 1 30 | USEast = 2 31 | Europe = 3 # EU West 32 | Korea = 4 33 | Singapore = 5 34 | Dubai = 6 35 | PerfectWorldTelecom = 12 # china 36 | PerfectWorldTelecomGuangdong = 17 # 37 | PerfectWorldTelecomZhejiang = 18 # 38 | PerfectWorldTelecomWuhan = 20 # 39 | PerfectWorldUnicom = 13 # 40 | PerfectWorldUnicomTianjin = 25 # 41 | Stockholm = 8 # Russia 42 | Brazil = 10 43 | Austria = 9 # EU East 44 | Australia = 7 45 | SouthAfrica = 11 46 | Chile = 14 47 | Peru = 15 48 | India = 16 49 | Japan = 19 50 | Taiwan = 37 51 | 52 | 53 | # Do not remove 54 | from sys import modules 55 | from enum import EnumMeta 56 | 57 | __all__ = [obj.__name__ 58 | for obj in modules[__name__].__dict__.values() 59 | if obj.__class__ is EnumMeta and obj.__name__ != 'IntEnum' 60 | ] 61 | 62 | del modules, EnumMeta 63 | -------------------------------------------------------------------------------- /dota2/enums.py: -------------------------------------------------------------------------------- 1 | 2 | from dota2.common_enums import * 3 | from dota2.proto_enums import * 4 | -------------------------------------------------------------------------------- /dota2/features/__init__.py: -------------------------------------------------------------------------------- 1 | from dota2.features.player import Player 2 | from dota2.features.match import Match 3 | from dota2.features.lobby import Lobby 4 | from dota2.features.chat import ChatBase 5 | from dota2.features.sharedobjects import SOBase 6 | from dota2.features.party import Party 7 | 8 | 9 | class FeatureBase(Player, Match, Lobby, Party, ChatBase, SOBase): 10 | """ 11 | This object is used to all high level functionality to Dota2Client. 12 | The features are seperated into submodules with a single class. 13 | """ 14 | pass 15 | -------------------------------------------------------------------------------- /dota2/features/chat.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import six 3 | from eventemitter import EventEmitter 4 | from dota2.enums import EDOTAGCMsg, DOTAChatChannelType_t 5 | from dota2.protobufs.dota_gcmessages_client_chat_pb2 import CMsgDOTAJoinChatChannelResponse,\ 6 | CMsgDOTAChatChannelFullUpdate,\ 7 | CMsgDOTAOtherJoinedChatChannel,\ 8 | CMsgDOTAOtherLeftChatChannel,\ 9 | CMsgDOTAChatChannelMemberUpdate 10 | 11 | 12 | class ChatBase(object): 13 | def __init__(self): 14 | super(ChatBase, self).__init__() 15 | name = "%s.channels" % self.__class__.__name__ 16 | self.channels = ChannelManager(self, name) 17 | 18 | 19 | class ChannelManager(EventEmitter): 20 | EVENT_JOINED_CHANNEL = 'channel_joined' 21 | """When the client join a channel. 22 | 23 | :param channel: channel instance 24 | :type channel: :class:`ChatChannel` 25 | """ 26 | EVENT_LEFT_CHANNEL = 'channel_left' 27 | """When the client leaves a channel. 28 | 29 | :param channel: channel instance 30 | :type channel: :class:`ChatChannel` 31 | """ 32 | EVENT_MESSAGE = 'message' 33 | """On a new channel message 34 | 35 | :param channel: channel instance 36 | :type channel: :class:`ChatChannel` 37 | :param message: message data 38 | :type message: `CMsgDOTAChatMessage `_ 39 | """ 40 | EVENT_CHANNEL_MEMBERS_UPDATE = 'members_update' 41 | """When users join/leave a channel 42 | 43 | :param channel: channel instance 44 | :type channel: :class:`ChatChannel` 45 | :param joined: list of members who joined 46 | :type joined: list 47 | :param left: list of members who left 48 | :type left: list 49 | """ 50 | 51 | def emit(self, event, *args): 52 | if event is not None: 53 | self._LOG.debug("Emit event: %s" % repr(event)) 54 | EventEmitter.emit(self, event, *args) 55 | 56 | def __init__(self, dota_client, logger_name): 57 | super(ChannelManager, self).__init__() 58 | 59 | self._LOG = logging.getLogger(logger_name if logger_name else self.__class__.__name__) 60 | self._dota = dota_client 61 | self._channels = {} 62 | self._channels_by_name = {} 63 | 64 | # register our handlers 65 | self._dota.on('notready', self._cleanup) 66 | self._dota.on(EDOTAGCMsg.EMsgGCJoinChatChannelResponse, self._handle_join_response) 67 | self._dota.on(EDOTAGCMsg.EMsgGCChatMessage, self._handle_message) 68 | self._dota.on(EDOTAGCMsg.EMsgGCOtherJoinedChannel, self._handle_members_update) 69 | self._dota.on(EDOTAGCMsg.EMsgGCOtherLeftChannel, self._handle_members_update) 70 | self._dota.on(EDOTAGCMsg.EMsgDOTAChatChannelMemberUpdate, self._handle_members_update) 71 | 72 | def __repr__(self): 73 | return "" % ( 74 | len(self), 75 | ) 76 | 77 | def _cleanup(self): 78 | self._channels.clear() 79 | self._channels_by_name.clear() 80 | 81 | def _remove_channel(self, channel_id): 82 | channel = self._channels.pop(channel_id, None) 83 | self._channels_by_name.pop((channel.name, channel.type), None) 84 | 85 | def __contains__(self, key): 86 | return (key in self._channels) or (key in self._channels_by_name) 87 | 88 | def __getitem__(self, key): 89 | if isinstance(key, tuple): 90 | return self._channels_by_name[key] 91 | else: 92 | return self._channels[key] 93 | 94 | def __len__(self): 95 | return len(self._channels) 96 | 97 | def __iter__(self): 98 | return six.itervalues(self._channels) 99 | 100 | def _handle_join_response(self, message): 101 | key = (message.channel_name, message.channel_type) 102 | self.emit(('join_result',) + key, message.result) 103 | 104 | if message.result == message.JOIN_SUCCESS: 105 | if message.channel_id in self: 106 | channel = self[message.channel_id] 107 | else: 108 | channel = ChatChannel(self, message) 109 | 110 | self._channels[channel.id] = channel 111 | self._channels_by_name[key] = channel 112 | 113 | self.emit(self.EVENT_JOINED_CHANNEL, channel) 114 | 115 | def _handle_message(self, message): 116 | if message.channel_id in self: 117 | self.emit(self.EVENT_MESSAGE, self[message.channel_id], message) 118 | 119 | def _handle_members_update(self, message): 120 | if message.channel_id in self: 121 | channel = self[message.channel_id] 122 | joined = [] 123 | left = [] 124 | 125 | if isinstance(message, CMsgDOTAOtherLeftChatChannel): 126 | left.append(message.steam_id or message.channel_user_id) 127 | elif isinstance(message, CMsgDOTAOtherJoinedChatChannel): 128 | joined.append(message.steam_id or message.channel_user_id) 129 | elif isinstance(message, CMsgDOTAChatChannelMemberUpdate): 130 | left = list(message.left_steam_ids) 131 | joined = list(map(lambda x: x.steam_id or x.channel_user_id, message.joined_members)) 132 | elif isinstance(message, CMsgDOTAChatChannelFullUpdate): 133 | pass 134 | 135 | channel._process_members_from_proto(message) 136 | 137 | if joined or left: 138 | self.emit(self.EVENT_CHANNEL_MEMBERS_UPDATE, channel, joined, left) 139 | 140 | def join_channel(self, channel_name, channel_type=DOTAChatChannelType_t.DOTAChannelType_Custom): 141 | """Join a chat channel 142 | 143 | :param channel_name: channel name 144 | :type channel_name: str 145 | :param channel_type: channel type 146 | :type channel_type: :class:`.DOTAChatChannelType_t` 147 | :return: join result 148 | :rtype: int 149 | 150 | Response event: :attr:`EVENT_JOINED_CHANNEL` 151 | """ 152 | if self._dota.verbose_debug: 153 | self._LOG.debug("Request to join chat channel: %s", channel_name) 154 | 155 | self._dota.send(EDOTAGCMsg.EMsgGCJoinChatChannel, { 156 | "channel_name": channel_name, 157 | "channel_type": channel_type 158 | }) 159 | 160 | resp = self.wait_event(('join_result', channel_name, channel_type), timeout=25) 161 | 162 | if resp: 163 | return resp[0] 164 | else: 165 | return None 166 | 167 | def join_lobby_channel(self): 168 | """ 169 | Join the lobby channel if the client is in a lobby. 170 | 171 | Response event: :attr:`EVENT_JOINED_CHANNEL` 172 | """ 173 | if self._dota.lobby: 174 | key = "Lobby_%s" % self._dota.lobby.lobby_id, DOTAChatChannelType_t.DOTAChannelType_Lobby 175 | return self.join_channel(*key) 176 | 177 | @property 178 | def lobby(self): 179 | """References lobby channel if client has joined it 180 | 181 | :return: channel instance 182 | :rtype: :class:`.ChatChannel` 183 | """ 184 | if self._dota.lobby: 185 | key = "Lobby_%s" % self._dota.lobby.lobby_id, DOTAChatChannelType_t.DOTAChannelType_Lobby 186 | return self._channels_by_name.get(key, None) 187 | 188 | def join_party_channel(self): 189 | """ 190 | Join the lobby channel if the client is in a lobby. 191 | 192 | Response event: :attr:`EVENT_JOINED_CHANNEL` 193 | """ 194 | if self._dota.party: 195 | key = "Party_%s" % self._dota.party.party_id, DOTAChatChannelType_t.DOTAChannelType_Party 196 | return self.join_channel(*key) 197 | 198 | @property 199 | def party(self): 200 | """References party channel if client has joined it 201 | 202 | :return: channel instance 203 | :rtype: :class:`.ChatChannel` 204 | """ 205 | if self._dota.party: 206 | key = "Party_%s" % self._dota.party.party_id, DOTAChatChannelType_t.DOTAChannelType_Party 207 | return self._channels_by_name.get(key, None) 208 | 209 | def get_channel_list(self): 210 | """ 211 | Requests a list of chat channels from the GC. 212 | 213 | :return: List of chat channels 214 | :rtype: `CMsgDOTAChatGetUserListResponse `_, ``None`` 215 | """ 216 | if self._dota.verbose_debug: 217 | self._LOG.debug("Requesting channel list from GC.") 218 | 219 | jobid = self._dota.send_job(EDOTAGCMsg.EMsgGCRequestChatChannelList, {}) 220 | resp = self._dota.wait_msg(jobid, timeout=25) 221 | 222 | return resp 223 | 224 | def leave_channel(self, channel_id): 225 | if channel_id in self: 226 | channel = self[channel_id] 227 | 228 | if self._dota.verbose_debug: 229 | self._LOG.debug("Leaving chat channel: %s", repr(channel)) 230 | 231 | self._dota.send(EDOTAGCMsg.EMsgGCLeaveChatChannel, { 232 | "channel_id": channel_id 233 | }) 234 | 235 | self._remove_channel(channel_id) 236 | self.emit(self.EVENT_LEFT_CHANNEL, channel) 237 | 238 | 239 | class ChatChannel(object): 240 | def __init__(self, channel_manager, join_data): 241 | self._manager = channel_manager 242 | self._dota = self._manager._dota 243 | self._LOG = self._manager._LOG 244 | 245 | self.members = {} 246 | self.id = join_data.channel_id 247 | self.name = join_data.channel_name 248 | self.type = join_data.channel_type 249 | self.user_id = join_data.channel_user_id 250 | self.max_members = join_data.max_members 251 | 252 | self._process_members_from_proto(join_data) 253 | 254 | def __repr__(self): 255 | return "" % ( 256 | self.id, 257 | repr(self.name), 258 | self.type, 259 | ) 260 | 261 | def _process_members_from_proto(self, data): 262 | if isinstance(data, CMsgDOTAOtherLeftChatChannel): 263 | self.members.pop(data.steam_id or data.channel_user_id, None) 264 | return 265 | elif isinstance(data, CMsgDOTAOtherJoinedChatChannel): 266 | members = [data] 267 | elif isinstance(data, CMsgDOTAJoinChatChannelResponse): 268 | members = data.members 269 | elif isinstance(data, CMsgDOTAChatChannelMemberUpdate): 270 | for steam_id in data.left_steam_ids: 271 | self.members.pop(steam_id, None) 272 | 273 | members = data.joined_members 274 | 275 | for member in members: 276 | self.members[member.steam_id or member.channel_user_id] = ( 277 | member.persona_name, 278 | member.status 279 | ) 280 | 281 | 282 | def leave(self): 283 | """Leave channel""" 284 | self._manager.leave_channel(self.id) 285 | 286 | def send(self, message): 287 | """Send a message to the channel 288 | 289 | :param message: message text 290 | :type message: str 291 | """ 292 | self._dota.send(EDOTAGCMsg.EMsgGCChatMessage, { 293 | "channel_id": self.id, 294 | "text": message 295 | }) 296 | 297 | def share_lobby(self): 298 | """Share current lobby to the channel""" 299 | if self._dota.lobby: 300 | self._dota.send(EDOTAGCMsg.EMsgGCChatMessage, { 301 | "channel_id": self.id, 302 | "share_lobby_id": self._dota.lobby.lobby_id, 303 | "share_lobby_passkey": self._dota.lobby.pass_key 304 | }) 305 | 306 | def flip_coin(self): 307 | """Flip a coin""" 308 | self._dota.send(EDOTAGCMsg.EMsgGCChatMessage, { 309 | "channel_id": self.id, 310 | "coin_flip": True 311 | }) 312 | 313 | def roll_dice(self, rollmin=1, rollmax=100): 314 | """Roll a dice 315 | 316 | :param rollmin: dice min value 317 | :type rollmin: int 318 | :param rollmax: dice max value 319 | :type rollmax: int 320 | """ 321 | self._dota.send(EDOTAGCMsg.EMsgGCChatMessage, { 322 | "channel_id": self.id, 323 | "dice_roll": { 324 | "roll_min": dmin, 325 | "roll_max": dmax 326 | } 327 | }) 328 | -------------------------------------------------------------------------------- /dota2/features/match.py: -------------------------------------------------------------------------------- 1 | from steam.enums import EResult 2 | from dota2.enums import EDOTAGCMsg 3 | 4 | class Match(object): 5 | def __init__(self): 6 | super(Match, self).__init__() 7 | 8 | # register our handlers 9 | self.on(EDOTAGCMsg.EMsgGCMatchmakingStatsResponse, self.__handle_mmstats) 10 | self.on(EDOTAGCMsg.EMsgGCToClientFindTopSourceTVGamesResponse, self.__handle_top_source_tv) 11 | self.on(EDOTAGCMsg.EMsgDOTAGetPlayerMatchHistoryResponse, self.__handle_player_match_history) 12 | self.on(EDOTAGCMsg.EMsgGCRequestMatches, self.__handle_matches) 13 | self.on(EDOTAGCMsg.EMsgClientToGCMatchesMinimalResponse, self.__handle_matches_minimal) 14 | 15 | def request_matchmaking_stats(self): 16 | """ 17 | Request matchmaking statistics 18 | 19 | Response event: ``matchmaking_stats`` 20 | 21 | :param message: `CMsgDOTAMatchmakingStatsResponse `_ 22 | :type message: proto message 23 | """ 24 | self.send(EDOTAGCMsg.EMsgGCMatchmakingStatsRequest) 25 | 26 | def __handle_mmstats(self, message): 27 | self.emit("matchmaking_stats", message) 28 | 29 | def request_match_details(self, match_id): 30 | """Request match details for a specific match 31 | 32 | .. note:: 33 | Rate limited to 100 requests/day 34 | 35 | :param match_id: match id 36 | :type match_id: :class:`int` 37 | :return: job event id 38 | :rtype: :class:`str` 39 | 40 | Response event: ``match_details`` 41 | 42 | :param match_id: match_id for response 43 | :type match_id: :class:`int` 44 | :param eresult: result enum 45 | :type eresult: :class:`steam.enums.common.EResult` 46 | :param match: `CMsgDOTAMatch `_ 47 | :type match: proto message 48 | """ 49 | jobid = self.send_job(EDOTAGCMsg.EMsgGCMatchDetailsRequest, { 50 | 'match_id': match_id, 51 | }) 52 | 53 | def wrap_match_details(message): 54 | eresult = EResult(message.result) 55 | match = message.match if eresult == EResult.OK else None 56 | self.emit('match_details', match_id, eresult, match) 57 | 58 | self.once(jobid, wrap_match_details) 59 | 60 | return jobid 61 | 62 | 63 | def request_matches(self, **kwargs): 64 | """Request matches. For arguments see `CMsgDOTARequestMatches `_ 65 | 66 | .. note:: 67 | Rate limited to 50 requests/day 68 | 69 | .. warning:: 70 | Some of the arguments don't work. Ask Valve 71 | 72 | :return: job event id 73 | :rtype: :class:`str` 74 | 75 | Response event: ``matches`` 76 | 77 | :param message: `CMsgDOTARequestMatchesResponse `_ 78 | :type message: proto message 79 | """ 80 | return self.send_job(EDOTAGCMsg.EMsgGCRequestMatches, kwargs) 81 | 82 | def __handle_matches(self, message): 83 | self.emit('matches', message) 84 | 85 | def request_matches_minimal(self, match_ids): 86 | """ 87 | Request matches with only minimal data. 88 | 89 | :param match_ids: match ids 90 | :type match_ids: :class:`list` 91 | :return: job event id 92 | :rtype: :class:`str` 93 | 94 | Response event: ``matches_minimal`` 95 | 96 | :param matches: list of `CMsgDOTAMatchMinimal `_ 97 | :type matches: :class:`list` 98 | 99 | """ 100 | return self.send_job(EDOTAGCMsg.EMsgClientToGCMatchesMinimalRequest, {'match_ids': match_ids}) 101 | 102 | def __handle_matches_minimal(self, message): 103 | self.emit('matches_minimal', message.matches) 104 | 105 | def request_top_source_tv_games(self, **kwargs): 106 | """ 107 | Find top source TV games. For arguments see `CMsgClientToGCFindTopSourceTVGames `_ 108 | 109 | Response event: ``top_source_tv_games`` 110 | 111 | :param response: `CMsgGCToClientFindTopSourceTVGamesResponse `_ 112 | :type response: proto message 113 | """ 114 | self.send(EDOTAGCMsg.EMsgClientToGCFindTopSourceTVGames, kwargs) 115 | 116 | def __handle_top_source_tv(self, message): 117 | self.emit("top_source_tv_games", message) 118 | 119 | def request_player_match_history(self, **kwargs): 120 | """Request player match history 121 | 122 | :param account_id: account id 123 | :type account_id: :class:`int` 124 | :param start_at_match_id: matches from before this match id (``0`` for latest) 125 | :type start_at_match_id: :class:`int` 126 | :param matches_requested: number of matches to return 127 | :type matches_requested: :class:`int` 128 | :param hero_id: filter by hero id 129 | :type hero_id: :class:`int` 130 | :param request_id: request id to match with the response with the request 131 | :type request_id: :class:`int` 132 | :param include_practice_matches: whether to include practive matches 133 | :type include_practice_matches: :class:`bool` 134 | :param include_custom_games: whether to include custom matches 135 | :type include_custom_games: :class:`bool` 136 | 137 | Response event: ``player_match_history`` 138 | 139 | :param request_id: request id from the reuqest 140 | :type request_id: :class:`int` 141 | :param matches: `CMsgDOTAGetPlayerMatchHistoryResponse.matches `_ 142 | :type matches: :class:`list` 143 | 144 | """ 145 | self.send(EDOTAGCMsg.EMsgDOTAGetPlayerMatchHistory, kwargs) 146 | 147 | def __handle_player_match_history(self, message): 148 | self.emit('player_match_history', message.request_id, message.matches) 149 | 150 | -------------------------------------------------------------------------------- /dota2/features/party.py: -------------------------------------------------------------------------------- 1 | from dota2.enums import EGCBaseMsg, EDOTAGCMsg, ESOType 2 | 3 | 4 | class Party(object): 5 | EVENT_PARTY_INVITE = 'party_invite' 6 | """When a party invite is receieved 7 | 8 | :param message: `CSODOTAPartyInvite `_ 9 | :type message: proto message 10 | """ 11 | EVENT_NEW_PARTY = 'new_party' 12 | """Entered a party, either by inviting someone or accepting an invite 13 | 14 | :param message: `CSODOTAParty `_ 15 | :type message: proto message 16 | """ 17 | EVENT_PARTY_CHANGED = 'party_changed' 18 | """Anything changes to the party state, leaving/entering/invites etc 19 | 20 | :param message: `CSODOTAParty `_ 21 | :type message: proto message 22 | """ 23 | EVENT_PARTY_REMOVED = 'party_removed' 24 | """Left party, either left, kicked or disbanded 25 | 26 | :param message: `CSODOTAParty `_ 27 | :type message: proto message 28 | """ 29 | EVENT_INVITATION_CREATED = 'invitation_created' 30 | """After inviting another user 31 | 32 | :param message: `CMsgInvitationCreated `_ 33 | :type message: proto message 34 | """ 35 | 36 | party = None 37 | 38 | def __init__(self): 39 | super(Party, self).__init__() 40 | self.on('notready', self.__party_cleanup) 41 | self.socache.on(('new', ESOType.CSODOTAPartyInvite), self.__handle_party_invite) 42 | self.socache.on(('new', ESOType.CSODOTAParty), self.__handle_party_new) 43 | self.socache.on(('updated', ESOType.CSODOTAParty), self.__handle_party_changed) 44 | self.socache.on(('removed', ESOType.CSODOTAParty), self.__handle_party_removed) 45 | 46 | self.on(EGCBaseMsg.EMsgGCInvitationCreated, self.__handle_invitation_created) 47 | 48 | def __party_cleanup(self): 49 | self.party = None 50 | 51 | def __handle_party_invite(self, message): 52 | self.emit('party_invite', message) 53 | 54 | def __handle_party_new(self, message): 55 | self.party = message 56 | self.emit('new_party', message) 57 | 58 | def __handle_party_changed(self, message): 59 | self.party = message 60 | self.emit('party_changed', message) 61 | 62 | def __handle_party_removed(self, message): 63 | self.party = None 64 | self.emit('party_removed', message) 65 | 66 | def __handle_invitation_created(self, message): 67 | self.emit('invitation_created', message) 68 | 69 | def respond_to_party_invite(self, party_id, accept=False): 70 | """ 71 | Respond to a party invite. 72 | 73 | :param party_id: party id 74 | :param accept: accept 75 | """ 76 | if self.verbose_debug: 77 | self._LOG.debug("Responding to party invite %s, accept: %s" % (party_id, accept)) 78 | 79 | self.send(EGCBaseMsg.EMsgGCPartyInviteResponse, { 80 | "party_id": party_id, 81 | "accept": accept 82 | }) 83 | 84 | def leave_party(self): 85 | """ 86 | Leaves the current party. 87 | 88 | :return: job event id 89 | :rtype: str 90 | """ 91 | if self.verbose_debug: 92 | self._LOG.debug("Leaving party.") 93 | 94 | self.send(EGCBaseMsg.EMsgGCLeaveParty, {}) 95 | 96 | def set_party_leader(self, steam_id): 97 | """ 98 | Set the new party leader. 99 | 100 | :param steam_id: steam_id 101 | :return: job event id 102 | :rtype: str 103 | """ 104 | if not self.party: return 105 | 106 | if self.verbose_debug: 107 | self._LOG.debug("Setting party leader: %s" % steam_id) 108 | 109 | self.send(EDOTAGCMsg.EMsgClientToGCSetPartyLeader, { 110 | "new_leader_steamid": steam_id 111 | }) 112 | 113 | def set_party_coach_flag(self, coach): 114 | """ 115 | Set the bot's status as a coach. 116 | 117 | :param coach: bool 118 | :return: job event id 119 | :rtype: str 120 | 121 | Response event: ``party_coach`` 122 | 123 | :param steam_id: steam_id for response 124 | :type steam_id: :class:`int` 125 | 126 | :param message: `CMsgDOTAPartyMemberSetCoach `_ proto message 127 | """ 128 | if not self.party or self.party.leader_id != self.steam.steam_id: return 129 | 130 | if self.verbose_debug: 131 | self._LOG.debug("Setting coach flag to: %s" % coach) 132 | 133 | self.send(EDOTAGCMsg.EMsgGCPartyMemberSetCoach, { 134 | "wants_coach": coach 135 | }) 136 | 137 | def invite_to_party(self, steam_id): 138 | """ 139 | Invites a player to a party. This will create a new party if you aren't in one. 140 | 141 | :param steam_id: steam_id 142 | :return: job event id 143 | :rtype: str 144 | 145 | Response event: ``invite_to_party`` 146 | 147 | :param message: `CMsgInvitationCreated `_ proto message 148 | """ 149 | if self.party and self.party.leader_id != self.steam.steam_id: return 150 | 151 | if self.verbose_debug: 152 | self._LOG.debug("Inviting %s to a party." % steam_id) 153 | 154 | jobid = self.send_job(EGCBaseMsg.EMsgGCInviteToParty, { 155 | "steam_id": steam_id 156 | }) 157 | 158 | @self.once(jobid) 159 | def wrap_invite_to_party(message): 160 | self.emit('invitation_created', message) 161 | 162 | return jobid 163 | 164 | def kick_from_party(self, steam_id): 165 | """ 166 | Kicks a player from the party. This will create a new party 167 | if you aren't in one. 168 | 169 | :param steam_id: steam_id 170 | :return: job event id 171 | :rtype: str 172 | 173 | Response event: ``kick_from_party`` 174 | 175 | :param steam_id: steam_id for response 176 | :type steam_id: :class:`int` 177 | 178 | :param message: `CMsgKickFromParty `_ proto message 179 | """ 180 | if (not self.party 181 | or self.party.leader_id != self.steam.steam_id 182 | or steam_id not in self.party.memeber_ids): return 183 | 184 | if self.verbose_debug: 185 | self._LOG.debug("Kicking %s from the party." % steam_id) 186 | 187 | self.send(EGCBaseMsg.EMsgGCKickFromParty, { 188 | "steam_id": steam_id 189 | }) 190 | -------------------------------------------------------------------------------- /dota2/features/player.py: -------------------------------------------------------------------------------- 1 | from steam.enums import EResult 2 | from dota2.enums import EDOTAGCMsg 3 | 4 | class Player(object): 5 | def __init__(self): 6 | super(Player, self).__init__() 7 | 8 | # register our handlers 9 | self.on(EDOTAGCMsg.EMsgGCPlayerInfo, self.__handle_player_info) 10 | self.on(EDOTAGCMsg.EMsgClientToGCLatestConductScorecard, self.__handle_conduct_scorecard) 11 | self.on(EDOTAGCMsg.EMsgGCGetHeroStandingsResponse, self.__handle_hero_standings) 12 | 13 | def request_profile(self, account_id): 14 | """ 15 | Request profile details 16 | 17 | :param account_id: steam account_id 18 | :type account_id: :class:`int` 19 | :return: job id 20 | :rtype: :class:`str` 21 | 22 | Response event: ``profile_data`` 23 | 24 | :param account_id: account_id from request 25 | :type account_id: :class:`int` 26 | :param message: `CMsgProfileResponse `_ 27 | :type message: proto message 28 | 29 | """ 30 | jobid = self.send_job(EDOTAGCMsg.EMsgProfileRequest, { 31 | 'account_id': account_id, 32 | }) 33 | 34 | def wrap_profile_data(message): 35 | self.emit("profile_data", account_id, message) 36 | 37 | self.once(jobid, wrap_profile_data) 38 | 39 | return jobid 40 | 41 | def request_gc_profile(self, account_id, request_name=False): 42 | """ 43 | Request profile details 44 | 45 | .. warning:: 46 | Disabled by Valve 47 | 48 | :param account_id: steam account_id 49 | :type account_id: :class:`int` 50 | :param request_name: whether to return name 51 | :type request_name: :class:`bool` 52 | :return: job id 53 | :rtype: :class:`str` 54 | 55 | Response event: ``gc_profile_data`` 56 | 57 | :param account_id: account_id from request 58 | :type account_id: :class:`int` 59 | :param eresult: result enum 60 | :type eresult: :class:`steam.enums.common.EResult` 61 | :param message: `CMsgDOTAProfileResponse `_ 62 | :type message: proto message 63 | 64 | """ 65 | jobid = self.send_job(EDOTAGCMsg.EMsgGCProfileRequest, { 66 | 'account_id': account_id, 67 | 'request_name': request_name, 68 | }) 69 | 70 | def wrap_profile_data(message): 71 | eresult = EResult(message.result) 72 | message = message if eresult == EResult.OK else None 73 | self.emit("gc_profile_data", account_id, eresult, message) 74 | 75 | self.once(jobid, wrap_profile_data) 76 | 77 | return jobid 78 | 79 | def request_profile_card(self, account_id): 80 | """ 81 | Request profile card 82 | 83 | :param account_id: steam account_id 84 | :type account_id: :class:`int` 85 | :return: job id 86 | :rtype: :class:`str` 87 | 88 | Response event: ``profile_card`` 89 | 90 | :param account_id: account_id from request 91 | :type account_id: :class:`int` 92 | :param message: `CMsgDOTAProfileCard `_ 93 | :type message: proto message 94 | 95 | """ 96 | jobid = self.send_job(EDOTAGCMsg.EMsgClientToGCGetProfileCard, { 97 | 'account_id': account_id, 98 | }) 99 | 100 | def wrap_profile_card(message): 101 | self.emit("profile_card", account_id, message) 102 | 103 | self.once(jobid, wrap_profile_card) 104 | 105 | return jobid 106 | 107 | def request_player_stats(self, account_id): 108 | """ 109 | Request players stats. These are located in the ``play style`` box on a player profie. 110 | 111 | :param account_id: steam account_id 112 | :type account_id: :class:`int` 113 | :return: job id 114 | :rtype: :class:`str` 115 | 116 | Response event: ``player_stats`` 117 | 118 | :param account_id: account_id from request 119 | :type account_id: :class:`int` 120 | :param message: `CMsgGCToClientPlayerStatsResponse `_ 121 | :type message: proto message 122 | 123 | """ 124 | jobid = self.send_job(EDOTAGCMsg.EMsgClientToGCPlayerStatsRequest, { 125 | 'account_id': account_id, 126 | }) 127 | 128 | def wrap_player_stats(message): 129 | self.emit("player_stats", account_id, message) 130 | 131 | self.once(jobid, wrap_player_stats) 132 | 133 | return jobid 134 | 135 | def request_player_info(self, account_ids): 136 | """ 137 | .. warning:: 138 | Disabled by Valve 139 | 140 | Request official player information 141 | 142 | :param account_id: A list of account ids 143 | :type account_id: :class:`list` 144 | 145 | Response event: ``player_info`` 146 | 147 | :param message: `CMsgGCPlayerInfo `_ 148 | :type message: proto message 149 | """ 150 | if not isinstance(account_ids, list): 151 | raise ValueError("Expected account_ids to be a list") 152 | 153 | self.send(EDOTAGCMsg.EMsgGCPlayerInfoRequest, { 154 | 'player_infos': map(lambda x: {'account_id': x}, account_ids), 155 | }) 156 | 157 | def __handle_player_info(self, message): 158 | self.emit("player_info", message) 159 | 160 | def request_conduct_scorecard(self): 161 | """ 162 | Request conduct scorecard, otherwise knows as conduct summary 163 | 164 | :return: job id 165 | :rtype: :class:`str` 166 | 167 | Response event: ``conduct_scorecard`` 168 | 169 | :param message: `CMsgPlayerConductScorecard `_ 170 | :type message: proto message 171 | """ 172 | return self.send_job(EDOTAGCMsg.EMsgClientToGCLatestConductScorecardRequest) 173 | 174 | def __handle_conduct_scorecard(self, message): 175 | self.emit("conduct_scorecard", message) 176 | 177 | def request_hero_standings(self): 178 | """ 179 | Request hero stands for the currently logged on account. 180 | This is the data from the ``stats`` tab on your profile. 181 | 182 | Response event: ``hero_standings`` 183 | 184 | :param message: `CMsgGCGetHeroStandingsResponse `_ 185 | :type message: proto message 186 | """ 187 | return self.send_job(EDOTAGCMsg.EMsgGCGetHeroStandings) 188 | 189 | def __handle_hero_standings(self, message): 190 | self.emit("hero_standings", message) 191 | -------------------------------------------------------------------------------- /dota2/features/sharedobjects.py: -------------------------------------------------------------------------------- 1 | """Essentially a :class:`dict` containing shared object caches. 2 | The objects are read-only, so don't change any values. 3 | The instance reference of individual objects will remain the same thought their lifetime. 4 | Individual objects can be accessed via their key, if they have one. 5 | 6 | .. note:: 7 | Some cache types don't have a key and only hold one object instance. 8 | Then only the the cache type is needed to access it. 9 | (e.g. ``CSOEconGameAccountClient``) 10 | 11 | .. code:: python 12 | 13 | dota_client.socache[ESOType.CSOEconItem] # dict with item objects, key = item id 14 | dota_client.socache[ESOType.CSOEconItem][123456] # item object 15 | 16 | dota_client.socache[ESOType.CSOEconGameAccountClient] # returns a CSOEconGameAccountClient object 17 | 18 | Events will be fired when individual objects are updated. 19 | Event key is a :class:`tuple`` in the following format: ``(event, cache_type)``. 20 | 21 | The available events are ``new``, ``updated``, and ``removed``. 22 | Each event has a single parameter, which is the object instance. 23 | Even when removed, there is object instance returned, usually only with the key field filled. 24 | 25 | .. code:: python 26 | 27 | @dota_client.socache.on(('new', ESOType.CSOEconItem)) 28 | def got_a_new_item(obj): 29 | print "Got a new item! Yay" 30 | print obj 31 | 32 | # access the item via socache at any time 33 | print dota_client.socache[ESOType.CSOEconItem][obj.id] 34 | 35 | """ 36 | import logging 37 | from eventemitter import EventEmitter 38 | from dota2.enums import EGCBaseClientMsg, ESOMsg, ESOType 39 | from dota2.protobufs import base_gcmessages_pb2 as _gc_base 40 | from dota2.protobufs import dota_gcmessages_client_pb2 as _gc_client 41 | from dota2.protobufs import dota_gcmessages_common_pb2 as _gc_common 42 | from dota2.protobufs import dota_gcmessages_common_match_management_pb2 as _gc_cmm 43 | 44 | 45 | def find_so_proto(type_id): 46 | """Resolves proto massage for given type_id 47 | 48 | :param type_id: SO type 49 | :type type_id: :class:`dota2.enums.ESOType` 50 | :returns: proto message or `None` 51 | """ 52 | if not isinstance(type_id, ESOType): 53 | return None 54 | 55 | proto = getattr(_gc_base, type_id.name, None) 56 | if proto is None: 57 | proto = getattr(_gc_client, type_id.name, None) 58 | if proto is None: 59 | proto = getattr(_gc_common, type_id.name, None) 60 | if proto is None: 61 | proto = getattr(_gc_cmm, type_id.name, None) 62 | 63 | return proto 64 | 65 | # hack to mark certain CSO as having no key 66 | class NO_KEY: 67 | pass 68 | 69 | so_key_fields = { 70 | _gc_base.CSOEconItem.DESCRIPTOR: ['id'], 71 | _gc_base.CSOEconGameAccountClient.DESCRIPTOR: NO_KEY, 72 | _gc_common.CSODOTAGameAccountClient.DESCRIPTOR: NO_KEY, 73 | } 74 | 75 | # key is either one or a number of fields marked with option 'key_field'=true in protos 76 | def get_so_key_fields(desc): 77 | if desc in so_key_fields: 78 | return so_key_fields[desc] 79 | else: 80 | fields = [] 81 | 82 | for field in desc.fields: 83 | for odesc, value in field.GetOptions().ListFields(): 84 | if odesc.name == 'key_field' and value == True: 85 | fields.append(field.name) 86 | 87 | so_key_fields[desc] = fields 88 | return fields 89 | 90 | def get_key_for_object(obj): 91 | key = get_so_key_fields(obj.DESCRIPTOR) 92 | 93 | if key is NO_KEY: 94 | return NO_KEY 95 | elif not key: 96 | return None 97 | elif len(key) == 1: 98 | return getattr(obj, key[0]) 99 | else: 100 | return tuple(map(lambda x: getattr(obj, x), key)) 101 | 102 | 103 | class SOBase(object): 104 | def __init__(self): 105 | super(SOBase, self).__init__() 106 | 107 | #: Shared Object Caches 108 | name = "%s.socache" % self.__class__.__name__ 109 | self.socache = SOCache(self, name) 110 | 111 | 112 | class SOCache(EventEmitter, dict): 113 | ESOType = ESOType #: expose ESOType 114 | 115 | file_version = None #: so file version 116 | 117 | def __init__(self, dota_client, logger_name): 118 | self._LOG = logging.getLogger(logger_name if logger_name else self.__class__.__name__) 119 | self._caches = {} 120 | self._dota = dota_client 121 | 122 | # register our handlers 123 | dota_client.on(ESOMsg.Create, self._handle_create) 124 | dota_client.on(ESOMsg.Update, self._handle_update) 125 | dota_client.on(ESOMsg.Destroy, self._handle_destroy) 126 | dota_client.on(ESOMsg.UpdateMultiple, self._handle_update_multiple) 127 | dota_client.on(ESOMsg.CacheSubscribed, self._handle_cache_subscribed) 128 | dota_client.on(ESOMsg.CacheUnsubscribed, self._handle_cache_unsubscribed) 129 | dota_client.on(EGCBaseClientMsg.EMsgGCClientWelcome, self._handle_client_welcome) 130 | dota_client.on('notready', self._handle_cleanup) 131 | 132 | def __hash__(self): 133 | # pretend that we are a hashable dict, lol 134 | # don't attach more than one SOCache per DotaClient 135 | return hash((self._dota, 42)) 136 | 137 | def __getitem__(self, key): 138 | try: 139 | key = ESOType(key) 140 | except ValueError: 141 | raise KeyError("%s" % key) 142 | if key not in self: 143 | self[key] = dict() 144 | return dict.__getitem__(self, key) 145 | 146 | def __repr__(self): 147 | return "" % repr(self._dota) 148 | 149 | def emit(self, event, *args): 150 | if event is not None: 151 | self._LOG.debug("Emit event: %s" % repr(event)) 152 | super(SOCache, self).emit(event, *args) 153 | 154 | def _handle_cleanup(self): 155 | for v in self.values(): 156 | if isinstance(v, dict): 157 | v.clear() 158 | self.clear() 159 | self._caches.clear() 160 | 161 | def _get_proto_for_type(self, type_id): 162 | try: 163 | type_id = ESOType(type_id) 164 | except ValueError: 165 | self._LOG.error("Unsupported type: %d" % type_id) 166 | return 167 | 168 | proto = find_so_proto(type_id) 169 | 170 | if proto is None: 171 | self._LOG.error("Unable to locate proto for: %s" % repr(type_id)) 172 | return 173 | 174 | return proto 175 | 176 | def _parse_object_data(self, type_id, object_data): 177 | proto = self._get_proto_for_type(type_id) 178 | 179 | if proto is None: 180 | return 181 | 182 | if not get_so_key_fields(proto.DESCRIPTOR): 183 | self._LOG.error("Unable to find key for %s" % type_id) 184 | return 185 | 186 | obj = proto.FromString(object_data) 187 | key = get_key_for_object(obj) 188 | 189 | return key, obj 190 | 191 | def _update_object(self, type_id, object_data): 192 | result = self._parse_object_data(type_id, object_data) 193 | 194 | if result: 195 | key, obj = result 196 | type_id = ESOType(type_id) 197 | 198 | if key is NO_KEY: 199 | if not isinstance(self[type_id], dict): 200 | self[type_id].CopyFrom(obj) 201 | obj = self[type_id] 202 | else: 203 | self[type_id] = obj 204 | else: 205 | if key in self[type_id]: 206 | self[type_id][key].CopyFrom(obj) 207 | obj = self[type_id][key] 208 | else: 209 | self[type_id][key] = obj 210 | 211 | return type_id, obj 212 | 213 | def _handle_create(self, message): 214 | result = self._update_object(message.type_id, message.object_data) 215 | if result: 216 | type_id, obj = result 217 | self.emit(('new', type_id), obj) 218 | 219 | def _handle_update(self, message): 220 | result = self._update_object(message.type_id, message.object_data) 221 | if result: 222 | type_id, obj = result 223 | 224 | if self._dota.verbose_debug: 225 | self._LOG.debug("Incoming: %s\n%s", repr(type_id), obj) 226 | 227 | self.emit(('updated', type_id), obj) 228 | 229 | def _handle_destroy(self, so): 230 | result = self._parse_object_data(so.type_id, so.object_data) 231 | if result: 232 | key, obj = result 233 | type_id = ESOType(so.type_id) 234 | current = None 235 | 236 | if key is NO_KEY: 237 | current = self.pop(type_id, None) 238 | else: 239 | current = self[type_id].pop(key, None) 240 | 241 | if current: current.CopyFrom(obj) 242 | 243 | self.emit(('removed', type_id), current or obj) 244 | 245 | def _handle_update_multiple(self, message): 246 | for so_object in message.objects_modified: 247 | self._handle_update(so_object) 248 | for so_object in message.objects_added: 249 | self._handle_create(so_object) 250 | for so_object in message.objects_removed: 251 | self._handle_destroy(so_object) 252 | 253 | def _handle_client_welcome(self, message): 254 | self.file_version = message.gc_socache_file_version 255 | 256 | for one in message.outofdate_subscribed_caches: 257 | self._handle_cache_subscribed(one) 258 | 259 | def _handle_cache_subscribed(self, message): 260 | cache_key = message.owner_soid.type, message.owner_soid.id 261 | self._caches.setdefault(cache_key, dict()) 262 | 263 | cache = self._caches[cache_key] 264 | cache['version'] = message.version 265 | cache.setdefault('type_ids', set()).update(map(lambda x: x.type_id, message.objects)) 266 | 267 | for objects in message.objects: 268 | for object_bytes in objects.object_data: 269 | result = self._update_object(objects.type_id, object_bytes) 270 | if not result: break 271 | 272 | type_id, obj = result 273 | 274 | if self._dota.verbose_debug: 275 | self._LOG.debug("Incoming: %s\n%s", repr(type_id), obj) 276 | 277 | self.emit(('new', type_id), obj) 278 | 279 | def _handle_cache_unsubscribed(self, message): 280 | cache_key = message.owner_soid.type, message.owner_soid.id 281 | 282 | if cache_key not in self._caches: return 283 | cache = self._caches[cache_key] 284 | 285 | for type_id in cache['type_ids']: 286 | if type_id in self: 287 | type_id = ESOType(type_id) 288 | 289 | if isinstance(self[type_id], dict): 290 | for key in list(self[type_id].keys()): 291 | self.emit(('removed', type_id), self[type_id].pop(key)) 292 | else: 293 | self.emit(('removed', type_id), self.pop(type_id)) 294 | 295 | del self[type_id] 296 | del self._caches[cache_key] 297 | 298 | 299 | -------------------------------------------------------------------------------- /dota2/msg.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various utility function for dealing with messages. 3 | 4 | """ 5 | 6 | from dota2.enums import EGCBaseClientMsg, EDOTAGCMsg, ESOMsg, EGCItemMsg, EGCBaseMsg 7 | from dota2.protobufs import ( 8 | base_gcmessages_pb2, 9 | gcsdk_gcmessages_pb2, 10 | dota_gcmessages_common_pb2, 11 | dota_gcmessages_client_pb2, 12 | dota_gcmessages_client_chat_pb2, 13 | dota_gcmessages_client_fantasy_pb2, 14 | dota_gcmessages_client_guild_pb2, 15 | dota_gcmessages_client_match_management_pb2, 16 | dota_gcmessages_client_team_pb2, 17 | dota_gcmessages_client_tournament_pb2, 18 | dota_gcmessages_client_watch_pb2, 19 | econ_gcmessages_pb2, 20 | ) 21 | 22 | 23 | def get_emsg_enum(emsg): 24 | """ 25 | Attempts to find the Enum for the given :class:`int` 26 | 27 | :param emsg: integer corresponding to a Enum 28 | :type emsg: :class:`int` 29 | :return: Enum if found, `emsg` if not 30 | :rtype: Enum, :class:`int` 31 | """ 32 | for enum in (EGCBaseClientMsg, 33 | EDOTAGCMsg, 34 | ESOMsg, 35 | EGCBaseMsg, 36 | EGCItemMsg, 37 | ): 38 | try: 39 | return enum(emsg) 40 | except ValueError: 41 | pass 42 | 43 | return emsg 44 | 45 | 46 | def find_proto(emsg): 47 | """ 48 | Attempts to find the protobuf message for a given Enum 49 | 50 | :param emsg: Enum corrensponding to a protobuf message 51 | :type emsg: `Enum` 52 | :return: protobuf message class 53 | """ 54 | 55 | if type(emsg) is int: 56 | return None 57 | 58 | if emsg in _proto_lookup_map: 59 | return _proto_lookup_map.get(emsg) 60 | 61 | if isinstance(emsg, ESOMsg): 62 | proto = _proto_lookup_map[emsg] = getattr(gcsdk_gcmessages_pb2, "CMsgSO%s" % emsg.name, None) 63 | return proto 64 | 65 | for module in (base_gcmessages_pb2, 66 | gcsdk_gcmessages_pb2, 67 | dota_gcmessages_common_pb2, 68 | dota_gcmessages_client_pb2, 69 | dota_gcmessages_client_chat_pb2, 70 | dota_gcmessages_client_fantasy_pb2, 71 | dota_gcmessages_client_guild_pb2, 72 | dota_gcmessages_client_match_management_pb2, 73 | dota_gcmessages_client_team_pb2, 74 | dota_gcmessages_client_tournament_pb2, 75 | dota_gcmessages_client_watch_pb2, 76 | econ_gcmessages_pb2, 77 | ): 78 | 79 | proto = getattr(module, emsg.name.replace("EMsg", "CMsg"), None) 80 | 81 | if proto is None: 82 | proto = getattr(module, emsg.name.replace("EMsgGC", "CMsgDOTA"), None) 83 | if proto is None: 84 | proto = getattr(module, emsg.name.replace("EMsgGCToClient", "CMsgDOTA"), None) 85 | if proto is None: 86 | proto = getattr(module, emsg.name.replace("EMsgGCToClient", "CMsg"), None) 87 | if proto is None: 88 | proto = getattr(module, emsg.name.replace("EMsgGC", "CMsg"), None) 89 | if proto is None: 90 | proto = getattr(module, emsg.name.replace("EMsgDOTA", "CMsg"), None) 91 | if proto is None: 92 | proto = getattr(module, emsg.name.replace("EMsg", "CMsgDOTA"), None) 93 | 94 | if proto is not None: 95 | _proto_lookup_map[emsg] = proto 96 | break 97 | 98 | return proto 99 | 100 | 101 | _proto_lookup_map = { 102 | EGCBaseClientMsg.EMsgGCClientConnectionStatus: gcsdk_gcmessages_pb2.CMsgConnectionStatus, 103 | EDOTAGCMsg.EMsgClientToGCGetProfileCardResponse: dota_gcmessages_common_pb2.CMsgDOTAProfileCard, 104 | EDOTAGCMsg.EMsgClientToGCLatestConductScorecardRequest: dota_gcmessages_client_pb2.CMsgPlayerConductScorecardRequest, 105 | EDOTAGCMsg.EMsgClientToGCLatestConductScorecard: dota_gcmessages_client_pb2.CMsgPlayerConductScorecard, 106 | ESOMsg.Create: gcsdk_gcmessages_pb2.CMsgSOSingleObject, 107 | ESOMsg.Update: gcsdk_gcmessages_pb2.CMsgSOSingleObject, 108 | ESOMsg.Destroy: gcsdk_gcmessages_pb2.CMsgSOSingleObject, 109 | ESOMsg.UpdateMultiple: gcsdk_gcmessages_pb2.CMsgSOMultipleObjects, 110 | EDOTAGCMsg.EMsgClientToGCEventGoalsRequest: dota_gcmessages_client_pb2.CMsgClientToGCGetEventGoals, 111 | EDOTAGCMsg.EMsgClientToGCEventGoalsResponse: dota_gcmessages_client_pb2.CMsgEventGoals, 112 | EDOTAGCMsg.EMsgClientToGCSetPartyLeader: dota_gcmessages_client_match_management_pb2.CMsgDOTASetGroupLeader, 113 | EDOTAGCMsg.EMsgGCOtherJoinedChannel: dota_gcmessages_client_chat_pb2.CMsgDOTAOtherJoinedChatChannel, 114 | EDOTAGCMsg.EMsgGCOtherLeftChannel: dota_gcmessages_client_chat_pb2.CMsgDOTAOtherLeftChatChannel, 115 | } 116 | -------------------------------------------------------------------------------- /dota2/protobufs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValvePython/dota2/6ccebc3689e107746ec32ce07fc2f5cacecc0e18/dota2/protobufs/__init__.py -------------------------------------------------------------------------------- /dota2/protobufs/econ_shared_enums_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: econ_shared_enums.proto 4 | 5 | import sys 6 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 7 | from google.protobuf.internal import enum_type_wrapper 8 | from google.protobuf import descriptor as _descriptor 9 | from google.protobuf import message as _message 10 | from google.protobuf import reflection as _reflection 11 | from google.protobuf import symbol_database as _symbol_database 12 | # @@protoc_insertion_point(imports) 13 | 14 | _sym_db = _symbol_database.Default() 15 | 16 | 17 | 18 | 19 | DESCRIPTOR = _descriptor.FileDescriptor( 20 | name='econ_shared_enums.proto', 21 | package='dota', 22 | syntax='proto2', 23 | serialized_options=_b('H\001\220\001\000'), 24 | serialized_pb=_b('\n\x17\x65\x63on_shared_enums.proto\x12\x04\x64ota\">\n\x11\x43MsgGenericResult\x12\x12\n\x07\x65result\x18\x01 \x01(\r:\x01\x32\x12\x15\n\rdebug_message\x18\x02 \x01(\t*,\n\x0e\x45GCEconBaseMsg\x12\x1a\n\x15k_EMsgGCGenericResult\x10\x93\x14*\x9b\x02\n\x0e\x45GCMsgResponse\x12\x16\n\x12k_EGCMsgResponseOK\x10\x00\x12\x1a\n\x16k_EGCMsgResponseDenied\x10\x01\x12\x1f\n\x1bk_EGCMsgResponseServerError\x10\x02\x12\x1b\n\x17k_EGCMsgResponseTimeout\x10\x03\x12\x1b\n\x17k_EGCMsgResponseInvalid\x10\x04\x12\x1b\n\x17k_EGCMsgResponseNoMatch\x10\x05\x12 \n\x1ck_EGCMsgResponseUnknownError\x10\x06\x12\x1f\n\x1bk_EGCMsgResponseNotLoggedOn\x10\x07\x12\x1a\n\x16k_EGCMsgFailedToCreate\x10\x08*\xa2\x01\n\x19\x45GCPartnerRequestResponse\x12\x17\n\x13k_EPartnerRequestOK\x10\x01\x12\x1f\n\x1bk_EPartnerRequestBadAccount\x10\x02\x12\x1e\n\x1ak_EPartnerRequestNotLinked\x10\x03\x12+\n\'k_EPartnerRequestUnsupportedPartnerType\x10\x04*\xc5\x05\n\x15\x45GCMsgUseItemResponse\x12$\n k_EGCMsgUseItemResponse_ItemUsed\x10\x00\x12.\n*k_EGCMsgUseItemResponse_GiftNoOtherPlayers\x10\x01\x12\'\n#k_EGCMsgUseItemResponse_ServerError\x10\x02\x12\x32\n.k_EGCMsgUseItemResponse_MiniGameAlreadyStarted\x10\x03\x12\x31\n-k_EGCMsgUseItemResponse_ItemUsed_ItemsGranted\x10\x04\x12\x37\n3k_EGCMsgUseItemResponse_DropRateBonusAlreadyGranted\x10\x05\x12\x30\n,k_EGCMsgUseItemResponse_NotInLowPriorityPool\x10\x06\x12.\n*k_EGCMsgUseItemResponse_NotHighEnoughLevel\x10\x07\x12*\n&k_EGCMsgUseItemResponse_EventNotActive\x10\x08\x12\x37\n3k_EGCMsgUseItemResponse_ItemUsed_EventPointsGranted\x10\t\x12.\n*k_EGCMsgUseItemResponse_MissingRequirement\x10\n\x12\x30\n,k_EGCMsgUseItemResponse_EmoticonUnlock_NoNew\x10\x0b\x12\x33\n/k_EGCMsgUseItemResponse_EmoticonUnlock_Complete\x10\x0c\x12/\n+k_EGCMsgUseItemResponse_ItemUsed_Compendium\x10\rB\x05H\x01\x90\x01\x00') 25 | ) 26 | 27 | _EGCECONBASEMSG = _descriptor.EnumDescriptor( 28 | name='EGCEconBaseMsg', 29 | full_name='dota.EGCEconBaseMsg', 30 | filename=None, 31 | file=DESCRIPTOR, 32 | values=[ 33 | _descriptor.EnumValueDescriptor( 34 | name='k_EMsgGCGenericResult', index=0, number=2579, 35 | serialized_options=None, 36 | type=None), 37 | ], 38 | containing_type=None, 39 | serialized_options=None, 40 | serialized_start=97, 41 | serialized_end=141, 42 | ) 43 | _sym_db.RegisterEnumDescriptor(_EGCECONBASEMSG) 44 | 45 | EGCEconBaseMsg = enum_type_wrapper.EnumTypeWrapper(_EGCECONBASEMSG) 46 | _EGCMSGRESPONSE = _descriptor.EnumDescriptor( 47 | name='EGCMsgResponse', 48 | full_name='dota.EGCMsgResponse', 49 | filename=None, 50 | file=DESCRIPTOR, 51 | values=[ 52 | _descriptor.EnumValueDescriptor( 53 | name='k_EGCMsgResponseOK', index=0, number=0, 54 | serialized_options=None, 55 | type=None), 56 | _descriptor.EnumValueDescriptor( 57 | name='k_EGCMsgResponseDenied', index=1, number=1, 58 | serialized_options=None, 59 | type=None), 60 | _descriptor.EnumValueDescriptor( 61 | name='k_EGCMsgResponseServerError', index=2, number=2, 62 | serialized_options=None, 63 | type=None), 64 | _descriptor.EnumValueDescriptor( 65 | name='k_EGCMsgResponseTimeout', index=3, number=3, 66 | serialized_options=None, 67 | type=None), 68 | _descriptor.EnumValueDescriptor( 69 | name='k_EGCMsgResponseInvalid', index=4, number=4, 70 | serialized_options=None, 71 | type=None), 72 | _descriptor.EnumValueDescriptor( 73 | name='k_EGCMsgResponseNoMatch', index=5, number=5, 74 | serialized_options=None, 75 | type=None), 76 | _descriptor.EnumValueDescriptor( 77 | name='k_EGCMsgResponseUnknownError', index=6, number=6, 78 | serialized_options=None, 79 | type=None), 80 | _descriptor.EnumValueDescriptor( 81 | name='k_EGCMsgResponseNotLoggedOn', index=7, number=7, 82 | serialized_options=None, 83 | type=None), 84 | _descriptor.EnumValueDescriptor( 85 | name='k_EGCMsgFailedToCreate', index=8, number=8, 86 | serialized_options=None, 87 | type=None), 88 | ], 89 | containing_type=None, 90 | serialized_options=None, 91 | serialized_start=144, 92 | serialized_end=427, 93 | ) 94 | _sym_db.RegisterEnumDescriptor(_EGCMSGRESPONSE) 95 | 96 | EGCMsgResponse = enum_type_wrapper.EnumTypeWrapper(_EGCMSGRESPONSE) 97 | _EGCPARTNERREQUESTRESPONSE = _descriptor.EnumDescriptor( 98 | name='EGCPartnerRequestResponse', 99 | full_name='dota.EGCPartnerRequestResponse', 100 | filename=None, 101 | file=DESCRIPTOR, 102 | values=[ 103 | _descriptor.EnumValueDescriptor( 104 | name='k_EPartnerRequestOK', index=0, number=1, 105 | serialized_options=None, 106 | type=None), 107 | _descriptor.EnumValueDescriptor( 108 | name='k_EPartnerRequestBadAccount', index=1, number=2, 109 | serialized_options=None, 110 | type=None), 111 | _descriptor.EnumValueDescriptor( 112 | name='k_EPartnerRequestNotLinked', index=2, number=3, 113 | serialized_options=None, 114 | type=None), 115 | _descriptor.EnumValueDescriptor( 116 | name='k_EPartnerRequestUnsupportedPartnerType', index=3, number=4, 117 | serialized_options=None, 118 | type=None), 119 | ], 120 | containing_type=None, 121 | serialized_options=None, 122 | serialized_start=430, 123 | serialized_end=592, 124 | ) 125 | _sym_db.RegisterEnumDescriptor(_EGCPARTNERREQUESTRESPONSE) 126 | 127 | EGCPartnerRequestResponse = enum_type_wrapper.EnumTypeWrapper(_EGCPARTNERREQUESTRESPONSE) 128 | _EGCMSGUSEITEMRESPONSE = _descriptor.EnumDescriptor( 129 | name='EGCMsgUseItemResponse', 130 | full_name='dota.EGCMsgUseItemResponse', 131 | filename=None, 132 | file=DESCRIPTOR, 133 | values=[ 134 | _descriptor.EnumValueDescriptor( 135 | name='k_EGCMsgUseItemResponse_ItemUsed', index=0, number=0, 136 | serialized_options=None, 137 | type=None), 138 | _descriptor.EnumValueDescriptor( 139 | name='k_EGCMsgUseItemResponse_GiftNoOtherPlayers', index=1, number=1, 140 | serialized_options=None, 141 | type=None), 142 | _descriptor.EnumValueDescriptor( 143 | name='k_EGCMsgUseItemResponse_ServerError', index=2, number=2, 144 | serialized_options=None, 145 | type=None), 146 | _descriptor.EnumValueDescriptor( 147 | name='k_EGCMsgUseItemResponse_MiniGameAlreadyStarted', index=3, number=3, 148 | serialized_options=None, 149 | type=None), 150 | _descriptor.EnumValueDescriptor( 151 | name='k_EGCMsgUseItemResponse_ItemUsed_ItemsGranted', index=4, number=4, 152 | serialized_options=None, 153 | type=None), 154 | _descriptor.EnumValueDescriptor( 155 | name='k_EGCMsgUseItemResponse_DropRateBonusAlreadyGranted', index=5, number=5, 156 | serialized_options=None, 157 | type=None), 158 | _descriptor.EnumValueDescriptor( 159 | name='k_EGCMsgUseItemResponse_NotInLowPriorityPool', index=6, number=6, 160 | serialized_options=None, 161 | type=None), 162 | _descriptor.EnumValueDescriptor( 163 | name='k_EGCMsgUseItemResponse_NotHighEnoughLevel', index=7, number=7, 164 | serialized_options=None, 165 | type=None), 166 | _descriptor.EnumValueDescriptor( 167 | name='k_EGCMsgUseItemResponse_EventNotActive', index=8, number=8, 168 | serialized_options=None, 169 | type=None), 170 | _descriptor.EnumValueDescriptor( 171 | name='k_EGCMsgUseItemResponse_ItemUsed_EventPointsGranted', index=9, number=9, 172 | serialized_options=None, 173 | type=None), 174 | _descriptor.EnumValueDescriptor( 175 | name='k_EGCMsgUseItemResponse_MissingRequirement', index=10, number=10, 176 | serialized_options=None, 177 | type=None), 178 | _descriptor.EnumValueDescriptor( 179 | name='k_EGCMsgUseItemResponse_EmoticonUnlock_NoNew', index=11, number=11, 180 | serialized_options=None, 181 | type=None), 182 | _descriptor.EnumValueDescriptor( 183 | name='k_EGCMsgUseItemResponse_EmoticonUnlock_Complete', index=12, number=12, 184 | serialized_options=None, 185 | type=None), 186 | _descriptor.EnumValueDescriptor( 187 | name='k_EGCMsgUseItemResponse_ItemUsed_Compendium', index=13, number=13, 188 | serialized_options=None, 189 | type=None), 190 | ], 191 | containing_type=None, 192 | serialized_options=None, 193 | serialized_start=595, 194 | serialized_end=1304, 195 | ) 196 | _sym_db.RegisterEnumDescriptor(_EGCMSGUSEITEMRESPONSE) 197 | 198 | EGCMsgUseItemResponse = enum_type_wrapper.EnumTypeWrapper(_EGCMSGUSEITEMRESPONSE) 199 | k_EMsgGCGenericResult = 2579 200 | k_EGCMsgResponseOK = 0 201 | k_EGCMsgResponseDenied = 1 202 | k_EGCMsgResponseServerError = 2 203 | k_EGCMsgResponseTimeout = 3 204 | k_EGCMsgResponseInvalid = 4 205 | k_EGCMsgResponseNoMatch = 5 206 | k_EGCMsgResponseUnknownError = 6 207 | k_EGCMsgResponseNotLoggedOn = 7 208 | k_EGCMsgFailedToCreate = 8 209 | k_EPartnerRequestOK = 1 210 | k_EPartnerRequestBadAccount = 2 211 | k_EPartnerRequestNotLinked = 3 212 | k_EPartnerRequestUnsupportedPartnerType = 4 213 | k_EGCMsgUseItemResponse_ItemUsed = 0 214 | k_EGCMsgUseItemResponse_GiftNoOtherPlayers = 1 215 | k_EGCMsgUseItemResponse_ServerError = 2 216 | k_EGCMsgUseItemResponse_MiniGameAlreadyStarted = 3 217 | k_EGCMsgUseItemResponse_ItemUsed_ItemsGranted = 4 218 | k_EGCMsgUseItemResponse_DropRateBonusAlreadyGranted = 5 219 | k_EGCMsgUseItemResponse_NotInLowPriorityPool = 6 220 | k_EGCMsgUseItemResponse_NotHighEnoughLevel = 7 221 | k_EGCMsgUseItemResponse_EventNotActive = 8 222 | k_EGCMsgUseItemResponse_ItemUsed_EventPointsGranted = 9 223 | k_EGCMsgUseItemResponse_MissingRequirement = 10 224 | k_EGCMsgUseItemResponse_EmoticonUnlock_NoNew = 11 225 | k_EGCMsgUseItemResponse_EmoticonUnlock_Complete = 12 226 | k_EGCMsgUseItemResponse_ItemUsed_Compendium = 13 227 | 228 | 229 | 230 | _CMSGGENERICRESULT = _descriptor.Descriptor( 231 | name='CMsgGenericResult', 232 | full_name='dota.CMsgGenericResult', 233 | filename=None, 234 | file=DESCRIPTOR, 235 | containing_type=None, 236 | fields=[ 237 | _descriptor.FieldDescriptor( 238 | name='eresult', full_name='dota.CMsgGenericResult.eresult', index=0, 239 | number=1, type=13, cpp_type=3, label=1, 240 | has_default_value=True, default_value=2, 241 | message_type=None, enum_type=None, containing_type=None, 242 | is_extension=False, extension_scope=None, 243 | serialized_options=None, file=DESCRIPTOR), 244 | _descriptor.FieldDescriptor( 245 | name='debug_message', full_name='dota.CMsgGenericResult.debug_message', index=1, 246 | number=2, type=9, cpp_type=9, label=1, 247 | has_default_value=False, default_value=_b("").decode('utf-8'), 248 | message_type=None, enum_type=None, containing_type=None, 249 | is_extension=False, extension_scope=None, 250 | serialized_options=None, file=DESCRIPTOR), 251 | ], 252 | extensions=[ 253 | ], 254 | nested_types=[], 255 | enum_types=[ 256 | ], 257 | serialized_options=None, 258 | is_extendable=False, 259 | syntax='proto2', 260 | extension_ranges=[], 261 | oneofs=[ 262 | ], 263 | serialized_start=33, 264 | serialized_end=95, 265 | ) 266 | 267 | DESCRIPTOR.message_types_by_name['CMsgGenericResult'] = _CMSGGENERICRESULT 268 | DESCRIPTOR.enum_types_by_name['EGCEconBaseMsg'] = _EGCECONBASEMSG 269 | DESCRIPTOR.enum_types_by_name['EGCMsgResponse'] = _EGCMSGRESPONSE 270 | DESCRIPTOR.enum_types_by_name['EGCPartnerRequestResponse'] = _EGCPARTNERREQUESTRESPONSE 271 | DESCRIPTOR.enum_types_by_name['EGCMsgUseItemResponse'] = _EGCMSGUSEITEMRESPONSE 272 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 273 | 274 | CMsgGenericResult = _reflection.GeneratedProtocolMessageType('CMsgGenericResult', (_message.Message,), dict( 275 | DESCRIPTOR = _CMSGGENERICRESULT, 276 | __module__ = 'econ_shared_enums_pb2' 277 | # @@protoc_insertion_point(class_scope:dota.CMsgGenericResult) 278 | )) 279 | _sym_db.RegisterMessage(CMsgGenericResult) 280 | 281 | 282 | DESCRIPTOR._options = None 283 | # @@protoc_insertion_point(module_scope) 284 | -------------------------------------------------------------------------------- /dota2/protobufs/gcsystemmsgs_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: gcsystemmsgs.proto 4 | 5 | import sys 6 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 7 | from google.protobuf.internal import enum_type_wrapper 8 | from google.protobuf import descriptor as _descriptor 9 | from google.protobuf import message as _message 10 | from google.protobuf import reflection as _reflection 11 | from google.protobuf import symbol_database as _symbol_database 12 | # @@protoc_insertion_point(imports) 13 | 14 | _sym_db = _symbol_database.Default() 15 | 16 | 17 | 18 | 19 | DESCRIPTOR = _descriptor.FileDescriptor( 20 | name='gcsystemmsgs.proto', 21 | package='dota', 22 | syntax='proto2', 23 | serialized_options=_b('H\001\220\001\000'), 24 | serialized_pb=_b('\n\x12gcsystemmsgs.proto\x12\x04\x64ota*\xf0\x01\n\x06\x45SOMsg\x12\x13\n\x0fk_ESOMsg_Create\x10\x15\x12\x13\n\x0fk_ESOMsg_Update\x10\x16\x12\x14\n\x10k_ESOMsg_Destroy\x10\x17\x12\x1c\n\x18k_ESOMsg_CacheSubscribed\x10\x18\x12\x1e\n\x1ak_ESOMsg_CacheUnsubscribed\x10\x19\x12\x1b\n\x17k_ESOMsg_UpdateMultiple\x10\x1a\x12%\n!k_ESOMsg_CacheSubscriptionRefresh\x10\x1c\x12$\n k_ESOMsg_CacheSubscribedUpToDate\x10\x1d*\xc2\x03\n\x10\x45GCBaseClientMsg\x12\x18\n\x13k_EMsgGCPingRequest\x10\xb9\x17\x12\x19\n\x14k_EMsgGCPingResponse\x10\xba\x17\x12&\n!k_EMsgGCToClientPollConvarRequest\x10\xbb\x17\x12\'\n\"k_EMsgGCToClientPollConvarResponse\x10\xbc\x17\x12\"\n\x1dk_EMsgGCCompressedMsgToClient\x10\xbd\x17\x12)\n$k_EMsgGCCompressedMsgToClient_Legacy\x10\x8b\x04\x12#\n\x1ek_EMsgGCToClientRequestDropped\x10\xbe\x17\x12\x1a\n\x15k_EMsgGCClientWelcome\x10\xa4\x1f\x12\x1a\n\x15k_EMsgGCServerWelcome\x10\xa5\x1f\x12\x18\n\x13k_EMsgGCClientHello\x10\xa6\x1f\x12\x18\n\x13k_EMsgGCServerHello\x10\xa7\x1f\x12#\n\x1ek_EMsgGCClientConnectionStatus\x10\xa9\x1f\x12#\n\x1ek_EMsgGCServerConnectionStatus\x10\xaa\x1f\x42\x05H\x01\x90\x01\x00') 25 | ) 26 | 27 | _ESOMSG = _descriptor.EnumDescriptor( 28 | name='ESOMsg', 29 | full_name='dota.ESOMsg', 30 | filename=None, 31 | file=DESCRIPTOR, 32 | values=[ 33 | _descriptor.EnumValueDescriptor( 34 | name='k_ESOMsg_Create', index=0, number=21, 35 | serialized_options=None, 36 | type=None), 37 | _descriptor.EnumValueDescriptor( 38 | name='k_ESOMsg_Update', index=1, number=22, 39 | serialized_options=None, 40 | type=None), 41 | _descriptor.EnumValueDescriptor( 42 | name='k_ESOMsg_Destroy', index=2, number=23, 43 | serialized_options=None, 44 | type=None), 45 | _descriptor.EnumValueDescriptor( 46 | name='k_ESOMsg_CacheSubscribed', index=3, number=24, 47 | serialized_options=None, 48 | type=None), 49 | _descriptor.EnumValueDescriptor( 50 | name='k_ESOMsg_CacheUnsubscribed', index=4, number=25, 51 | serialized_options=None, 52 | type=None), 53 | _descriptor.EnumValueDescriptor( 54 | name='k_ESOMsg_UpdateMultiple', index=5, number=26, 55 | serialized_options=None, 56 | type=None), 57 | _descriptor.EnumValueDescriptor( 58 | name='k_ESOMsg_CacheSubscriptionRefresh', index=6, number=28, 59 | serialized_options=None, 60 | type=None), 61 | _descriptor.EnumValueDescriptor( 62 | name='k_ESOMsg_CacheSubscribedUpToDate', index=7, number=29, 63 | serialized_options=None, 64 | type=None), 65 | ], 66 | containing_type=None, 67 | serialized_options=None, 68 | serialized_start=29, 69 | serialized_end=269, 70 | ) 71 | _sym_db.RegisterEnumDescriptor(_ESOMSG) 72 | 73 | ESOMsg = enum_type_wrapper.EnumTypeWrapper(_ESOMSG) 74 | _EGCBASECLIENTMSG = _descriptor.EnumDescriptor( 75 | name='EGCBaseClientMsg', 76 | full_name='dota.EGCBaseClientMsg', 77 | filename=None, 78 | file=DESCRIPTOR, 79 | values=[ 80 | _descriptor.EnumValueDescriptor( 81 | name='k_EMsgGCPingRequest', index=0, number=3001, 82 | serialized_options=None, 83 | type=None), 84 | _descriptor.EnumValueDescriptor( 85 | name='k_EMsgGCPingResponse', index=1, number=3002, 86 | serialized_options=None, 87 | type=None), 88 | _descriptor.EnumValueDescriptor( 89 | name='k_EMsgGCToClientPollConvarRequest', index=2, number=3003, 90 | serialized_options=None, 91 | type=None), 92 | _descriptor.EnumValueDescriptor( 93 | name='k_EMsgGCToClientPollConvarResponse', index=3, number=3004, 94 | serialized_options=None, 95 | type=None), 96 | _descriptor.EnumValueDescriptor( 97 | name='k_EMsgGCCompressedMsgToClient', index=4, number=3005, 98 | serialized_options=None, 99 | type=None), 100 | _descriptor.EnumValueDescriptor( 101 | name='k_EMsgGCCompressedMsgToClient_Legacy', index=5, number=523, 102 | serialized_options=None, 103 | type=None), 104 | _descriptor.EnumValueDescriptor( 105 | name='k_EMsgGCToClientRequestDropped', index=6, number=3006, 106 | serialized_options=None, 107 | type=None), 108 | _descriptor.EnumValueDescriptor( 109 | name='k_EMsgGCClientWelcome', index=7, number=4004, 110 | serialized_options=None, 111 | type=None), 112 | _descriptor.EnumValueDescriptor( 113 | name='k_EMsgGCServerWelcome', index=8, number=4005, 114 | serialized_options=None, 115 | type=None), 116 | _descriptor.EnumValueDescriptor( 117 | name='k_EMsgGCClientHello', index=9, number=4006, 118 | serialized_options=None, 119 | type=None), 120 | _descriptor.EnumValueDescriptor( 121 | name='k_EMsgGCServerHello', index=10, number=4007, 122 | serialized_options=None, 123 | type=None), 124 | _descriptor.EnumValueDescriptor( 125 | name='k_EMsgGCClientConnectionStatus', index=11, number=4009, 126 | serialized_options=None, 127 | type=None), 128 | _descriptor.EnumValueDescriptor( 129 | name='k_EMsgGCServerConnectionStatus', index=12, number=4010, 130 | serialized_options=None, 131 | type=None), 132 | ], 133 | containing_type=None, 134 | serialized_options=None, 135 | serialized_start=272, 136 | serialized_end=722, 137 | ) 138 | _sym_db.RegisterEnumDescriptor(_EGCBASECLIENTMSG) 139 | 140 | EGCBaseClientMsg = enum_type_wrapper.EnumTypeWrapper(_EGCBASECLIENTMSG) 141 | k_ESOMsg_Create = 21 142 | k_ESOMsg_Update = 22 143 | k_ESOMsg_Destroy = 23 144 | k_ESOMsg_CacheSubscribed = 24 145 | k_ESOMsg_CacheUnsubscribed = 25 146 | k_ESOMsg_UpdateMultiple = 26 147 | k_ESOMsg_CacheSubscriptionRefresh = 28 148 | k_ESOMsg_CacheSubscribedUpToDate = 29 149 | k_EMsgGCPingRequest = 3001 150 | k_EMsgGCPingResponse = 3002 151 | k_EMsgGCToClientPollConvarRequest = 3003 152 | k_EMsgGCToClientPollConvarResponse = 3004 153 | k_EMsgGCCompressedMsgToClient = 3005 154 | k_EMsgGCCompressedMsgToClient_Legacy = 523 155 | k_EMsgGCToClientRequestDropped = 3006 156 | k_EMsgGCClientWelcome = 4004 157 | k_EMsgGCServerWelcome = 4005 158 | k_EMsgGCClientHello = 4006 159 | k_EMsgGCServerHello = 4007 160 | k_EMsgGCClientConnectionStatus = 4009 161 | k_EMsgGCServerConnectionStatus = 4010 162 | 163 | 164 | DESCRIPTOR.enum_types_by_name['ESOMsg'] = _ESOMSG 165 | DESCRIPTOR.enum_types_by_name['EGCBaseClientMsg'] = _EGCBASECLIENTMSG 166 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 167 | 168 | 169 | DESCRIPTOR._options = None 170 | # @@protoc_insertion_point(module_scope) 171 | -------------------------------------------------------------------------------- /dota2/utils/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | def replay_url(match_id, cluster, replay_salt, app_id=570): 3 | """Form url for match replay 4 | 5 | :param match_id: match id 6 | :type match_id: :class:`int` 7 | :param cluster: cluster the match is saved on 8 | :type cluster: :class:`int` 9 | :param replay_salt: salt linked to the replay 10 | :type replay_salt: :class:`int` 11 | :param app_id: (optional) app_id for dota 12 | :type app_id: :class:`int` 13 | :return: url to download the replay of a specific match 14 | :rtype: :class:`str` 15 | """ 16 | return 'http://replay{0}.valve.net/{1}/{2}_{3}.dem.bz2'.format(cluster, app_id, match_id, replay_salt) 17 | 18 | 19 | def replay_url_from_match(match, app_id=570): 20 | """Form url for match replay 21 | 22 | :param match: `CMsgDOTAMatch `_ 23 | :type match: proto message 24 | :param app_id: (optional) app_id for dota 25 | :type app_id: :class:`int` 26 | :return: url to download the replay of a specific match, None if match has not all the information 27 | :rtype: :class:`str`, :class:`None` 28 | """ 29 | if match.HasField('match_id') and match.HasField('cluster') and match.HasField('replay_salt'): 30 | return replay_url(match.match_id, match.cluster, match.replay_salt, app_id) 31 | 32 | 33 | def metadata_url(match_id, cluster, replay_salt, app_id=570): 34 | """Form url for match metadata file 35 | 36 | :param match_id: match id 37 | :type match_id: :class:`int` 38 | :param cluster: cluster the match is saved on 39 | :type cluster: :class:`int` 40 | :param replay_salt: salt linked to the replay 41 | :type replay_salt: :class:`int` 42 | :param app_id: (optional) app_id for dota 43 | :type app_id: :class:`int` 44 | :return: url to download the metadata of a specific match 45 | :rtype: :class:`str` 46 | """ 47 | return 'http://replay{0}.valve.net/{1}/{2}_{3}.meta.bz2'.format(cluster, app_id, match_id, replay_salt) 48 | 49 | 50 | def metadata_url_from_match(match, app_id=570): 51 | """Form url for match metadata file 52 | 53 | :param match: `CMsgDOTAMatch `_ 54 | :type match: proto message 55 | :param app_id: (optional) app_id for dota 56 | :type app_id: :class:`int` 57 | :return: url to download the metadata of a specific match, None if match has not all the information 58 | :rtype: :class:`str`, :class:`None` 59 | """ 60 | if match.HasField('match_id') and match.HasField('cluster') and match.HasField('replay_salt'): 61 | return metadata_url(match.match_id, match.cluster, match.replay_salt, app_id) 62 | -------------------------------------------------------------------------------- /gen_enum_from_protos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import re 4 | from keyword import kwlist 5 | from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper 6 | from dota2 import common_enums 7 | 8 | kwlist = set(kwlist + ['None']) 9 | 10 | _proto_modules = [ 11 | 'base_gcmessages_pb2', 12 | 'dota_client_enums_pb2', 13 | 'dota_gcmessages_client_pb2', 14 | 'dota_gcmessages_client_fantasy_pb2', 15 | 'dota_gcmessages_client_match_management_pb2', 16 | 'dota_gcmessages_client_team_pb2', 17 | 'dota_gcmessages_client_tournament_pb2', 18 | 'dota_gcmessages_common_pb2', 19 | 'dota_gcmessages_common_match_management_pb2', 20 | 'dota_gcmessages_msgid_pb2', 21 | 'dota_shared_enums_pb2', 22 | 'gcsdk_gcmessages_pb2', 23 | 'gcsystemmsgs_pb2', 24 | 'steammessages_pb2', 25 | 'econ_gcmessages_pb2', 26 | 'econ_shared_enums_pb2', 27 | ] 28 | 29 | _proto_module = __import__("dota2.protobufs", globals(), locals(), _proto_modules, 0) 30 | 31 | classes = {} 32 | 33 | for name in _proto_modules: 34 | 35 | proto = getattr(_proto_module, name) 36 | gvars = globals() 37 | 38 | for class_name, value in proto.__dict__.items(): 39 | if not isinstance(value, EnumTypeWrapper) or hasattr(common_enums, class_name): 40 | continue 41 | 42 | attrs_starting_with_number = False 43 | attrs = {} 44 | 45 | for ikey, ivalue in value.items(): 46 | ikey = re.sub(r'^(k_)?(%s_)?' % class_name, '', ikey) 47 | attrs[ikey] = ivalue 48 | 49 | if ikey[0:1].isdigit() or ikey in kwlist: 50 | attrs_starting_with_number = True 51 | 52 | classes[class_name] = attrs, attrs_starting_with_number 53 | 54 | # Generate print out 55 | 56 | print("from enum import IntEnum") 57 | 58 | for class_name, (attrs, attrs_starting_with_number) in sorted(classes.items(), key=lambda x: x[0].lower()): 59 | if attrs_starting_with_number: 60 | print("\n%s = IntEnum(%r, {" % (class_name, class_name)) 61 | for ikey, ivalue in attrs.items(): 62 | print(" %r: %r," % (ikey, ivalue)) 63 | print(" })") 64 | else: 65 | print("\nclass {class_name}(IntEnum):".format(class_name=class_name)) 66 | for ikey, ivalue in attrs.items(): 67 | print(" {} = {}".format(ikey, ivalue)) 68 | 69 | print("\n__all__ = [") 70 | 71 | for class_name in sorted(classes, key=lambda x: x.lower()): 72 | print(" %r," % class_name) 73 | 74 | print(" ]") 75 | -------------------------------------------------------------------------------- /protobuf_list.txt: -------------------------------------------------------------------------------- 1 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/steammessages.proto 2 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/gcsystemmsgs.proto 3 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/base_gcmessages.proto 4 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/gcsdk_gcmessages.proto 5 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_client_enums.proto 6 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client.proto 7 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client_chat.proto 8 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client_fantasy.proto 9 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client_guild.proto 10 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client_match_management.proto 11 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client_team.proto 12 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client_tournament.proto 13 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_client_watch.proto 14 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_common.proto 15 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_common_match_management.proto 16 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_gcmessages_msgid.proto 17 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_shared_enums.proto 18 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/econ_gcmessages.proto 19 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/econ_shared_enums.proto 20 | https://github.com/SteamDatabase/GameTracking-Dota2/raw/master/Protobufs/dota_match_metadata.proto 21 | -------------------------------------------------------------------------------- /protobufs/base_gcmessages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | import "steammessages.proto"; 4 | import "gcsdk_gcmessages.proto"; 5 | 6 | option optimize_for = SPEED; 7 | option py_generic_services = false; 8 | 9 | enum EGCBaseMsg { 10 | k_EMsgGCSystemMessage = 4001; 11 | k_EMsgGCReplicateConVars = 4002; 12 | k_EMsgGCConVarUpdated = 4003; 13 | k_EMsgGCInviteToParty = 4501; 14 | k_EMsgGCInvitationCreated = 4502; 15 | k_EMsgGCPartyInviteResponse = 4503; 16 | k_EMsgGCKickFromParty = 4504; 17 | k_EMsgGCLeaveParty = 4505; 18 | k_EMsgGCServerAvailable = 4506; 19 | k_EMsgGCClientConnectToServer = 4507; 20 | k_EMsgGCGameServerInfo = 4508; 21 | k_EMsgGCError = 4509; 22 | k_EMsgGCLANServerAvailable = 4511; 23 | k_EMsgGCInviteToLobby = 4512; 24 | k_EMsgGCLobbyInviteResponse = 4513; 25 | k_EMsgGCToClientPollFileRequest = 4514; 26 | k_EMsgGCToClientPollFileResponse = 4515; 27 | k_EMsgGCToGCPerformManualOp = 4516; 28 | k_EMsgGCToGCPerformManualOpCompleted = 4517; 29 | k_EMsgGCToGCReloadServerRegionSettings = 4518; 30 | k_EMsgGCAdditionalWelcomeMsgList = 4519; 31 | } 32 | 33 | enum EGCBaseProtoObjectTypes { 34 | k_EProtoObjectPartyInvite = 1001; 35 | k_EProtoObjectLobbyInvite = 1002; 36 | } 37 | 38 | enum ECustomGameInstallStatus { 39 | k_ECustomGameInstallStatus_Unknown = 0; 40 | k_ECustomGameInstallStatus_Ready = 1; 41 | k_ECustomGameInstallStatus_Busy = 2; 42 | k_ECustomGameInstallStatus_FailedGeneric = 101; 43 | k_ECustomGameInstallStatus_FailedInternalError = 102; 44 | k_ECustomGameInstallStatus_RequestedTimestampTooOld = 103; 45 | k_ECustomGameInstallStatus_RequestedTimestampTooNew = 104; 46 | k_ECustomGameInstallStatus_CRCMismatch = 105; 47 | k_ECustomGameInstallStatus_FailedSteam = 106; 48 | k_ECustomGameInstallStatus_FailedCanceled = 107; 49 | } 50 | 51 | message CGCStorePurchaseInit_LineItem { 52 | optional uint32 item_def_id = 1; 53 | optional uint32 quantity = 2; 54 | optional uint32 cost_in_local_currency = 3; 55 | optional uint32 purchase_type = 4; 56 | optional uint64 source_reference_id = 5; 57 | } 58 | 59 | message CMsgGCStorePurchaseInit { 60 | optional string country = 1; 61 | optional int32 language = 2; 62 | optional int32 currency = 3; 63 | repeated dota.CGCStorePurchaseInit_LineItem line_items = 4; 64 | } 65 | 66 | message CMsgGCStorePurchaseInitResponse { 67 | optional int32 result = 1; 68 | optional uint64 txn_id = 2; 69 | } 70 | 71 | message CMsgSystemBroadcast { 72 | optional string message = 1; 73 | } 74 | 75 | message CMsgClientPingData { 76 | repeated fixed32 relay_codes = 4 [packed = true]; 77 | repeated uint32 relay_pings = 5 [packed = true]; 78 | repeated uint32 region_codes = 8 [packed = true]; 79 | repeated uint32 region_pings = 9 [packed = true]; 80 | optional uint32 region_ping_failed_bitmask = 10; 81 | } 82 | 83 | message CMsgInviteToParty { 84 | optional fixed64 steam_id = 1; 85 | optional uint32 client_version = 2; 86 | optional uint32 team_id = 3; 87 | optional bool as_coach = 4; 88 | optional dota.CMsgClientPingData ping_data = 5; 89 | } 90 | 91 | message CMsgInviteToLobby { 92 | optional fixed64 steam_id = 1; 93 | optional uint32 client_version = 2; 94 | } 95 | 96 | message CMsgInvitationCreated { 97 | optional uint64 group_id = 1; 98 | optional fixed64 steam_id = 2; 99 | optional bool user_offline = 3; 100 | } 101 | 102 | message CMsgPartyInviteResponse { 103 | optional uint64 party_id = 1; 104 | optional bool accept = 2; 105 | optional uint32 client_version = 3; 106 | optional dota.CMsgClientPingData ping_data = 8; 107 | } 108 | 109 | message CMsgLobbyInviteResponse { 110 | optional fixed64 lobby_id = 1; 111 | optional bool accept = 2; 112 | optional uint32 client_version = 3; 113 | optional fixed64 custom_game_crc = 6; 114 | optional fixed32 custom_game_timestamp = 7; 115 | } 116 | 117 | message CMsgKickFromParty { 118 | optional fixed64 steam_id = 1; 119 | } 120 | 121 | message CMsgLeaveParty { 122 | } 123 | 124 | message CMsgCustomGameInstallStatus { 125 | optional dota.ECustomGameInstallStatus status = 1 [default = k_ECustomGameInstallStatus_Unknown]; 126 | optional string message = 2; 127 | optional fixed32 latest_timestamp_from_steam = 3; 128 | } 129 | 130 | message CMsgServerAvailable { 131 | optional dota.CMsgCustomGameInstallStatus custom_game_install_status = 1; 132 | } 133 | 134 | message CMsgLANServerAvailable { 135 | optional fixed64 lobby_id = 1; 136 | } 137 | 138 | message CSOEconGameAccountClient { 139 | optional uint32 additional_backpack_slots = 1 [default = 0]; 140 | optional bool trial_account = 2 [default = false]; 141 | optional bool eligible_for_online_play = 3 [default = true]; 142 | optional bool need_to_choose_most_helpful_friend = 4; 143 | optional bool in_coaches_list = 5; 144 | optional fixed32 trade_ban_expiration = 6; 145 | optional fixed32 duel_ban_expiration = 7; 146 | optional bool made_first_purchase = 9 [default = false]; 147 | } 148 | 149 | message CSOItemCriteriaCondition { 150 | optional int32 op = 1; 151 | optional string field = 2; 152 | optional bool required = 3; 153 | optional float float_value = 4; 154 | optional string string_value = 5; 155 | } 156 | 157 | message CSOItemCriteria { 158 | optional uint32 item_level = 1; 159 | optional int32 item_quality = 2; 160 | optional bool item_level_set = 3; 161 | optional bool item_quality_set = 4; 162 | optional uint32 initial_inventory = 5; 163 | optional uint32 initial_quantity = 6; 164 | optional bool ignore_enabled_flag = 8; 165 | repeated dota.CSOItemCriteriaCondition conditions = 9; 166 | optional bool recent_only = 10; 167 | } 168 | 169 | message CSOItemRecipe { 170 | optional uint32 def_index = 1; 171 | optional string name = 2; 172 | optional string n_a = 3; 173 | optional string desc_inputs = 4; 174 | optional string desc_outputs = 5; 175 | optional string di_a = 6; 176 | optional string di_b = 7; 177 | optional string di_c = 8; 178 | optional string do_a = 9; 179 | optional string do_b = 10; 180 | optional string do_c = 11; 181 | optional bool requires_all_same_class = 12; 182 | optional bool requires_all_same_slot = 13; 183 | optional int32 class_usage_for_output = 14; 184 | optional int32 slot_usage_for_output = 15; 185 | optional int32 set_for_output = 16; 186 | repeated dota.CSOItemCriteria input_items_criteria = 20; 187 | repeated dota.CSOItemCriteria output_items_criteria = 21; 188 | repeated uint32 input_item_dupe_counts = 22; 189 | } 190 | 191 | message CMsgApplyStrangePart { 192 | optional uint64 strange_part_item_id = 1; 193 | optional uint64 item_item_id = 2; 194 | } 195 | 196 | message CMsgApplyPennantUpgrade { 197 | optional uint64 upgrade_item_id = 1; 198 | optional uint64 pennant_item_id = 2; 199 | } 200 | 201 | message CMsgApplyEggEssence { 202 | optional uint64 essence_item_id = 1; 203 | optional uint64 egg_item_id = 2; 204 | } 205 | 206 | message CSOEconItemAttribute { 207 | optional uint32 def_index = 1; 208 | optional uint32 value = 2; 209 | optional bytes value_bytes = 3; 210 | } 211 | 212 | message CSOEconItemEquipped { 213 | optional uint32 new_class = 1; 214 | optional uint32 new_slot = 2; 215 | } 216 | 217 | message CSOEconItem { 218 | optional uint64 id = 1; 219 | optional uint32 account_id = 2; 220 | optional uint32 inventory = 3; 221 | optional uint32 def_index = 4; 222 | optional uint32 quantity = 5 [default = 1]; 223 | optional uint32 level = 6 [default = 1]; 224 | optional uint32 quality = 7 [default = 4]; 225 | optional uint32 flags = 8 [default = 0]; 226 | optional uint32 origin = 9 [default = 0]; 227 | repeated dota.CSOEconItemAttribute attribute = 12; 228 | optional dota.CSOEconItem interior_item = 13; 229 | optional uint32 style = 15 [default = 0]; 230 | optional uint64 original_id = 16 [default = 0]; 231 | repeated dota.CSOEconItemEquipped equipped_state = 18; 232 | } 233 | 234 | message CMsgSortItems { 235 | optional uint32 sort_type = 1; 236 | } 237 | 238 | message CSOEconClaimCode { 239 | optional uint32 account_id = 1; 240 | optional uint32 code_type = 2; 241 | optional uint32 time_acquired = 3; 242 | optional string code = 4; 243 | } 244 | 245 | message CMsgUpdateItemSchema { 246 | optional bytes items_game = 1; 247 | optional fixed32 item_schema_version = 2; 248 | optional string items_game_url = 3; 249 | } 250 | 251 | message CMsgGCError { 252 | optional string error_text = 1; 253 | } 254 | 255 | message CMsgRequestInventoryRefresh { 256 | } 257 | 258 | message CMsgConVarValue { 259 | optional string name = 1; 260 | optional string value = 2; 261 | } 262 | 263 | message CMsgReplicateConVars { 264 | repeated dota.CMsgConVarValue convars = 1; 265 | } 266 | 267 | message CMsgItemAcknowledged { 268 | optional uint32 account_id = 1; 269 | optional uint32 inventory = 2; 270 | optional uint32 def_index = 3; 271 | optional uint32 quality = 4; 272 | optional uint32 rarity = 5; 273 | optional uint32 origin = 6; 274 | } 275 | 276 | message CMsgSetItemPositions { 277 | message ItemPosition { 278 | optional uint64 item_id = 1; 279 | optional uint32 position = 2; 280 | } 281 | 282 | repeated dota.CMsgSetItemPositions.ItemPosition item_positions = 1; 283 | } 284 | 285 | message CMsgGCNameItemNotification { 286 | optional fixed64 player_steamid = 1; 287 | optional uint32 item_def_index = 2; 288 | optional string item_name_custom = 3; 289 | } 290 | 291 | message CMsgGCClientDisplayNotification { 292 | optional string notification_title_localization_key = 1; 293 | optional string notification_body_localization_key = 2; 294 | repeated string body_substring_keys = 3; 295 | repeated string body_substring_values = 4; 296 | } 297 | 298 | message CMsgGCShowItemsPickedUp { 299 | optional fixed64 player_steamid = 1; 300 | } 301 | 302 | message CMsgGCIncrementKillCountResponse { 303 | optional uint32 killer_account_id = 1 [(key_field) = true]; 304 | optional uint32 num_kills = 2; 305 | optional uint32 item_def = 3; 306 | optional uint32 level_type = 4; 307 | } 308 | 309 | message CSOEconItemDropRateBonus { 310 | optional uint32 account_id = 1 [(key_field) = true]; 311 | optional fixed32 expiration_date = 2; 312 | optional float bonus = 3 [(key_field) = true]; 313 | optional uint32 bonus_count = 4; 314 | optional uint64 item_id = 5; 315 | optional uint32 def_index = 6; 316 | optional uint32 seconds_left = 7; 317 | optional uint32 booster_type = 8 [(key_field) = true]; 318 | } 319 | 320 | message CSOEconItemLeagueViewPass { 321 | optional uint32 account_id = 1 [(key_field) = true]; 322 | optional uint32 league_id = 2 [(key_field) = true]; 323 | optional uint32 itemindex = 4; 324 | optional uint32 grant_reason = 5; 325 | } 326 | 327 | message CSOEconItemEventTicket { 328 | optional uint32 account_id = 1; 329 | optional uint32 event_id = 2; 330 | optional uint64 item_id = 3; 331 | } 332 | 333 | message CSOEconItemTournamentPassport { 334 | optional uint32 account_id = 1; 335 | optional uint32 league_id = 2; 336 | optional uint64 item_id = 3; 337 | optional uint32 original_purchaser_id = 4; 338 | optional uint32 passports_bought = 5; 339 | optional uint32 version = 6; 340 | optional uint32 def_index = 7; 341 | optional uint32 reward_flags = 8; 342 | } 343 | 344 | message CMsgGCStorePurchaseCancel { 345 | optional uint64 txn_id = 1; 346 | } 347 | 348 | message CMsgGCStorePurchaseCancelResponse { 349 | optional uint32 result = 1; 350 | } 351 | 352 | message CMsgGCStorePurchaseFinalize { 353 | optional uint64 txn_id = 1; 354 | } 355 | 356 | message CMsgGCStorePurchaseFinalizeResponse { 357 | optional uint32 result = 1; 358 | repeated uint64 item_ids = 2; 359 | } 360 | 361 | message CMsgGCToGCBannedWordListUpdated { 362 | optional uint32 group_id = 1; 363 | } 364 | 365 | message CMsgGCToGCDirtySDOCache { 366 | optional uint32 sdo_type = 1; 367 | optional uint64 key_uint64 = 2; 368 | } 369 | 370 | message CMsgGCToGCDirtyMultipleSDOCache { 371 | optional uint32 sdo_type = 1; 372 | repeated uint64 key_uint64 = 2; 373 | } 374 | 375 | message CMsgGCToGCApplyLocalizationDiff { 376 | optional uint32 language = 1; 377 | optional string packed_diff = 2; 378 | } 379 | 380 | message CMsgGCToGCApplyLocalizationDiffResponse { 381 | optional bool success = 1; 382 | } 383 | 384 | message CMsgGCCollectItem { 385 | optional uint64 collection_item_id = 1; 386 | optional uint64 subject_item_id = 2; 387 | } 388 | 389 | message CMsgSDONoMemcached { 390 | } 391 | 392 | message CMsgGCToGCUpdateSQLKeyValue { 393 | optional string key_name = 1; 394 | } 395 | 396 | message CMsgGCServerVersionUpdated { 397 | optional uint32 server_version = 1; 398 | } 399 | 400 | message CMsgGCClientVersionUpdated { 401 | optional uint32 client_version = 1; 402 | } 403 | 404 | message CMsgGCToGCWebAPIAccountChanged { 405 | } 406 | 407 | message CMsgRecipeComponent { 408 | optional uint64 subject_item_id = 1; 409 | optional uint64 attribute_index = 2; 410 | } 411 | 412 | message CMsgFulfillDynamicRecipeComponent { 413 | optional uint64 tool_item_id = 1; 414 | repeated dota.CMsgRecipeComponent consumption_components = 2; 415 | } 416 | 417 | message CMsgGCClientMarketDataRequest { 418 | optional uint32 user_currency = 1; 419 | } 420 | 421 | message CMsgGCClientMarketDataEntry { 422 | optional uint32 item_def_index = 1; 423 | optional uint32 item_quality = 2; 424 | optional uint32 item_sell_listings = 3; 425 | optional uint32 price_in_local_currency = 4; 426 | } 427 | 428 | message CMsgGCClientMarketData { 429 | repeated dota.CMsgGCClientMarketDataEntry entries = 1; 430 | } 431 | 432 | message CMsgExtractGems { 433 | optional uint64 tool_item_id = 1; 434 | optional uint64 item_item_id = 2; 435 | optional uint32 item_socket_id = 3 [default = 65535]; 436 | } 437 | 438 | message CMsgExtractGemsResponse { 439 | enum EExtractGems { 440 | k_ExtractGems_Succeeded = 0; 441 | k_ExtractGems_Failed_ToolIsInvalid = 1; 442 | k_ExtractGems_Failed_ItemIsInvalid = 2; 443 | k_ExtractGems_Failed_ToolCannotRemoveGem = 3; 444 | k_ExtractGems_Failed_FailedToRemoveGem = 4; 445 | } 446 | 447 | optional uint64 item_id = 1; 448 | optional dota.CMsgExtractGemsResponse.EExtractGems response = 2 [default = k_ExtractGems_Succeeded]; 449 | } 450 | 451 | message CMsgAddSocket { 452 | optional uint64 tool_item_id = 1; 453 | optional uint64 item_item_id = 2; 454 | optional bool unusual = 3; 455 | } 456 | 457 | message CMsgAddSocketResponse { 458 | enum EAddSocket { 459 | k_AddSocket_Succeeded = 0; 460 | k_AddSocket_Failed_ToolIsInvalid = 1; 461 | k_AddSocket_Failed_ItemCannotBeSocketed = 2; 462 | k_AddSocket_Failed_FailedToAddSocket = 3; 463 | } 464 | 465 | optional uint64 item_id = 1; 466 | repeated uint32 updated_socket_index = 2; 467 | optional dota.CMsgAddSocketResponse.EAddSocket response = 3 [default = k_AddSocket_Succeeded]; 468 | } 469 | 470 | message CMsgAddItemToSocketData { 471 | optional uint64 gem_item_id = 1; 472 | optional uint32 socket_index = 2; 473 | } 474 | 475 | message CMsgAddItemToSocket { 476 | optional uint64 item_item_id = 1; 477 | repeated dota.CMsgAddItemToSocketData gems_to_socket = 2; 478 | } 479 | 480 | message CMsgAddItemToSocketResponse { 481 | enum EAddGem { 482 | k_AddGem_Succeeded = 0; 483 | k_AddGem_Failed_GemIsInvalid = 1; 484 | k_AddGem_Failed_ItemIsInvalid = 2; 485 | k_AddGem_Failed_FailedToAddGem = 3; 486 | k_AddGem_Failed_InvalidGemTypeForSocket = 4; 487 | k_AddGem_Failed_InvalidGemTypeForHero = 5; 488 | k_AddGem_Failed_InvalidGemTypeForSlot = 6; 489 | k_AddGem_Failed_SocketContainsUnremovableGem = 7; 490 | } 491 | 492 | optional uint64 item_item_id = 1; 493 | repeated uint32 updated_socket_index = 2; 494 | optional dota.CMsgAddItemToSocketResponse.EAddGem response = 3 [default = k_AddGem_Succeeded]; 495 | } 496 | 497 | message CMsgResetStrangeGemCount { 498 | optional uint64 item_item_id = 1; 499 | optional uint32 socket_index = 2; 500 | } 501 | 502 | message CMsgResetStrangeGemCountResponse { 503 | enum EResetGem { 504 | k_ResetGem_Succeeded = 0; 505 | k_ResetGem_Failed_FailedToResetGem = 1; 506 | k_ResetGem_Failed_ItemIsInvalid = 2; 507 | k_ResetGem_Failed_InvalidSocketId = 3; 508 | k_ResetGem_Failed_SocketCannotBeReset = 4; 509 | } 510 | 511 | optional dota.CMsgResetStrangeGemCountResponse.EResetGem response = 1 [default = k_ResetGem_Succeeded]; 512 | } 513 | 514 | message CMsgGCToClientPollFileRequest { 515 | optional string file_name = 1; 516 | optional uint32 client_version = 2; 517 | optional uint32 poll_id = 3; 518 | } 519 | 520 | message CMsgGCToClientPollFileResponse { 521 | optional uint32 poll_id = 1; 522 | optional uint32 file_size = 2; 523 | } 524 | 525 | message CMsgGCToGCPerformManualOp { 526 | optional uint64 op_id = 1; 527 | optional uint32 group_code = 2; 528 | } 529 | 530 | message CMsgGCToGCPerformManualOpCompleted { 531 | optional bool success = 1; 532 | optional uint32 source_gc = 2; 533 | } 534 | 535 | message CMsgGCToGCReloadServerRegionSettings { 536 | } 537 | 538 | message CMsgGCAdditionalWelcomeMsgList { 539 | repeated dota.CExtraMsgBlock welcome_messages = 1; 540 | } 541 | -------------------------------------------------------------------------------- /protobufs/dota_client_enums.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | option optimize_for = SPEED; 4 | option py_generic_services = false; 5 | 6 | enum ETournamentTemplate { 7 | k_ETournamentTemplate_None = 0; 8 | k_ETournamentTemplate_AutomatedWin3 = 1; 9 | } 10 | 11 | enum ETournamentGameState { 12 | k_ETournamentGameState_Unknown = 0; 13 | k_ETournamentGameState_Canceled = 1; 14 | k_ETournamentGameState_Scheduled = 2; 15 | k_ETournamentGameState_Active = 3; 16 | k_ETournamentGameState_RadVictory = 20; 17 | k_ETournamentGameState_DireVictory = 21; 18 | k_ETournamentGameState_RadVictoryByForfeit = 22; 19 | k_ETournamentGameState_DireVictoryByForfeit = 23; 20 | k_ETournamentGameState_ServerFailure = 40; 21 | k_ETournamentGameState_NotNeeded = 41; 22 | } 23 | 24 | enum ETournamentTeamState { 25 | k_ETournamentTeamState_Unknown = 0; 26 | k_ETournamentTeamState_Node1 = 1; 27 | k_ETournamentTeamState_NodeMax = 1024; 28 | k_ETournamentTeamState_Eliminated = 14003; 29 | k_ETournamentTeamState_Forfeited = 14004; 30 | k_ETournamentTeamState_Finished1st = 15001; 31 | k_ETournamentTeamState_Finished2nd = 15002; 32 | k_ETournamentTeamState_Finished3rd = 15003; 33 | k_ETournamentTeamState_Finished4th = 15004; 34 | k_ETournamentTeamState_Finished5th = 15005; 35 | k_ETournamentTeamState_Finished6th = 15006; 36 | k_ETournamentTeamState_Finished7th = 15007; 37 | k_ETournamentTeamState_Finished8th = 15008; 38 | k_ETournamentTeamState_Finished9th = 15009; 39 | k_ETournamentTeamState_Finished10th = 15010; 40 | k_ETournamentTeamState_Finished11th = 15011; 41 | k_ETournamentTeamState_Finished12th = 15012; 42 | k_ETournamentTeamState_Finished13th = 15013; 43 | k_ETournamentTeamState_Finished14th = 15014; 44 | k_ETournamentTeamState_Finished15th = 15015; 45 | k_ETournamentTeamState_Finished16th = 15016; 46 | } 47 | 48 | enum ETournamentState { 49 | k_ETournamentState_Unknown = 0; 50 | k_ETournamentState_CanceledByAdmin = 1; 51 | k_ETournamentState_Completed = 2; 52 | k_ETournamentState_Merged = 3; 53 | k_ETournamentState_ServerFailure = 4; 54 | k_ETournamentState_TeamAbandoned = 5; 55 | k_ETournamentState_TeamTimeoutForfeit = 6; 56 | k_ETournamentState_TeamTimeoutRefund = 7; 57 | k_ETournamentState_ServerFailureGrantedVictory = 8; 58 | k_ETournamentState_TeamTimeoutGrantedVictory = 9; 59 | k_ETournamentState_InProgress = 100; 60 | k_ETournamentState_WaitingToMerge = 101; 61 | } 62 | 63 | enum ETournamentNodeState { 64 | k_ETournamentNodeState_Unknown = 0; 65 | k_ETournamentNodeState_Canceled = 1; 66 | k_ETournamentNodeState_TeamsNotYetAssigned = 2; 67 | k_ETournamentNodeState_InBetweenGames = 3; 68 | k_ETournamentNodeState_GameInProgress = 4; 69 | k_ETournamentNodeState_A_Won = 5; 70 | k_ETournamentNodeState_B_Won = 6; 71 | k_ETournamentNodeState_A_WonByForfeit = 7; 72 | k_ETournamentNodeState_B_WonByForfeit = 8; 73 | k_ETournamentNodeState_A_Bye = 9; 74 | k_ETournamentNodeState_A_Abandoned = 10; 75 | k_ETournamentNodeState_ServerFailure = 11; 76 | k_ETournamentNodeState_A_TimeoutForfeit = 12; 77 | k_ETournamentNodeState_A_TimeoutRefund = 13; 78 | } 79 | 80 | enum EDOTAGroupMergeResult { 81 | k_EDOTAGroupMergeResult_OK = 0; 82 | k_EDOTAGroupMergeResult_FAILED_GENERIC = 1; 83 | k_EDOTAGroupMergeResult_NOT_LEADER = 2; 84 | k_EDOTAGroupMergeResult_TOO_MANY_PLAYERS = 3; 85 | k_EDOTAGroupMergeResult_TOO_MANY_COACHES = 4; 86 | k_EDOTAGroupMergeResult_ENGINE_MISMATCH = 5; 87 | k_EDOTAGroupMergeResult_NO_SUCH_GROUP = 6; 88 | k_EDOTAGroupMergeResult_OTHER_GROUP_NOT_OPEN = 7; 89 | k_EDOTAGroupMergeResult_ALREADY_INVITED = 8; 90 | k_EDOTAGroupMergeResult_NOT_INVITED = 9; 91 | } 92 | 93 | enum EPartyBeaconType { 94 | k_EPartyBeaconType_Available = 0; 95 | k_EPartyBeaconType_Joinable = 1; 96 | } 97 | -------------------------------------------------------------------------------- /protobufs/dota_gcmessages_client_chat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | import "dota_shared_enums.proto"; 4 | 5 | option optimize_for = SPEED; 6 | option py_generic_services = false; 7 | 8 | message CMsgClientToGCPrivateChatInvite { 9 | optional string private_chat_channel_name = 1; 10 | optional uint32 invited_account_id = 2; 11 | } 12 | 13 | message CMsgClientToGCPrivateChatKick { 14 | optional string private_chat_channel_name = 1; 15 | optional uint32 kick_account_id = 2; 16 | } 17 | 18 | message CMsgClientToGCPrivateChatPromote { 19 | optional string private_chat_channel_name = 1; 20 | optional uint32 promote_account_id = 2; 21 | } 22 | 23 | message CMsgClientToGCPrivateChatDemote { 24 | optional string private_chat_channel_name = 1; 25 | optional uint32 demote_account_id = 2; 26 | } 27 | 28 | message CMsgGCToClientPrivateChatResponse { 29 | enum Result { 30 | SUCCESS = 0; 31 | FAILURE_CREATION_LOCK = 1; 32 | FAILURE_SQL_TRANSACTION = 2; 33 | FAILURE_SDO_LOAD = 3; 34 | FAILURE_NO_PERMISSION = 4; 35 | FAILURE_ALREADY_MEMBER = 5; 36 | FAILURE_NOT_A_MEMBER = 7; 37 | FAILURE_NO_REMAINING_ADMINS = 8; 38 | FAILURE_NO_ROOM = 9; 39 | FAILURE_CREATION_RATE_LIMITED = 10; 40 | FAILURE_UNKNOWN_CHANNEL_NAME = 11; 41 | FAILURE_UNKNOWN_USER = 12; 42 | FAILURE_UNKNOWN_ERROR = 13; 43 | FAILURE_CANNOT_KICK_ADMIN = 14; 44 | FAILURE_ALREADY_ADMIN = 15; 45 | } 46 | 47 | optional string private_chat_channel_name = 1; 48 | optional dota.CMsgGCToClientPrivateChatResponse.Result result = 2 [default = SUCCESS]; 49 | optional string username = 3; 50 | } 51 | 52 | message CMsgClientToGCPrivateChatInfoRequest { 53 | optional string private_chat_channel_name = 1; 54 | } 55 | 56 | message CMsgGCToClientPrivateChatInfoResponse { 57 | message Member { 58 | optional uint32 account_id = 1; 59 | optional string name = 2; 60 | optional uint32 status = 3; 61 | } 62 | 63 | optional string private_chat_channel_name = 1; 64 | repeated dota.CMsgGCToClientPrivateChatInfoResponse.Member members = 2; 65 | optional uint32 creator = 3; 66 | optional uint32 creation_date = 4; 67 | } 68 | 69 | message CMsgDOTAJoinChatChannel { 70 | optional string channel_name = 2; 71 | optional dota.DOTAChatChannelType_t channel_type = 4 [default = DOTAChannelType_Regional]; 72 | optional bool silent_rejection = 5; 73 | } 74 | 75 | message CMsgDOTALeaveChatChannel { 76 | optional uint64 channel_id = 1; 77 | } 78 | 79 | message CMsgGCChatReportPublicSpam { 80 | optional uint64 channel_id = 1; 81 | optional uint32 channel_user_id = 2; 82 | } 83 | 84 | message CMsgDOTAClientIgnoredUser { 85 | optional uint32 ignored_account_id = 1; 86 | } 87 | 88 | message CMsgDOTAChatModeratorBan { 89 | optional uint64 channel_id = 1; 90 | optional uint32 account_id = 2; 91 | optional uint32 duration = 3; 92 | } 93 | 94 | message CMsgDOTAChatMessage { 95 | message DiceRoll { 96 | optional int32 roll_min = 1; 97 | optional int32 roll_max = 2; 98 | optional int32 result = 3; 99 | } 100 | 101 | message TriviaAnswered { 102 | optional uint32 question_id = 1; 103 | optional uint32 answer_index = 2; 104 | optional uint32 party_questions_correct = 3; 105 | optional uint32 party_questions_viewed = 4; 106 | optional uint32 party_trivia_points = 5; 107 | } 108 | 109 | optional uint32 account_id = 1; 110 | optional uint64 channel_id = 2; 111 | optional string persona_name = 3; 112 | optional string text = 4; 113 | optional uint32 timestamp = 5; 114 | optional uint32 suggest_invite_account_id = 6; 115 | optional string suggest_invite_name = 7; 116 | optional uint32 fantasy_draft_owner_account_id = 8; 117 | optional uint32 fantasy_draft_player_account_id = 9; 118 | optional uint32 event_id = 10; 119 | optional bool suggest_invite_to_lobby = 11; 120 | optional uint32 event_points = 12; 121 | optional bool coin_flip = 13; 122 | optional int32 player_id = 14 [default = -1]; 123 | optional uint32 share_profile_account_id = 15; 124 | optional uint32 channel_user_id = 16; 125 | optional dota.CMsgDOTAChatMessage.DiceRoll dice_roll = 17; 126 | optional uint64 share_party_id = 18; 127 | optional uint64 share_lobby_id = 19; 128 | optional uint64 share_lobby_custom_game_id = 20; 129 | optional string share_lobby_passkey = 21; 130 | optional uint32 private_chat_channel_id = 22; 131 | optional uint32 status = 23; 132 | optional bool legacy_battle_cup_victory = 24; 133 | optional uint32 battle_cup_streak = 29; 134 | optional uint32 badge_level = 25; 135 | optional uint32 suggest_pick_hero_id = 26; 136 | optional string suggest_pick_hero_role = 27; 137 | optional uint32 suggest_ban_hero_id = 30; 138 | optional dota.CMsgDOTAChatMessage.TriviaAnswered trivia_answer = 32; 139 | optional uint32 requested_ability_id = 33; 140 | optional uint32 chat_flags = 34; 141 | optional bool started_finding_match = 35; 142 | optional bool ctrl_is_down = 36; 143 | } 144 | 145 | message CMsgDOTAChatMember { 146 | optional fixed64 steam_id = 1; 147 | optional string persona_name = 2; 148 | optional uint32 channel_user_id = 3; 149 | optional uint32 status = 4; 150 | } 151 | 152 | message CMsgDOTAJoinChatChannelResponse { 153 | enum Result { 154 | JOIN_SUCCESS = 0; 155 | INVALID_CHANNEL_TYPE = 1; 156 | ACCOUNT_NOT_FOUND = 2; 157 | ACH_FAILED = 3; 158 | USER_IN_TOO_MANY_CHANNELS = 4; 159 | RATE_LIMIT_EXCEEDED = 5; 160 | CHANNEL_FULL = 6; 161 | CHANNEL_FULL_OVERFLOWED = 7; 162 | FAILED_TO_ADD_USER = 8; 163 | CHANNEL_TYPE_DISABLED = 9; 164 | PRIVATE_CHAT_CREATE_FAILED = 10; 165 | PRIVATE_CHAT_NO_PERMISSION = 11; 166 | PRIVATE_CHAT_CREATE_LOCK_FAILED = 12; 167 | PRIVATE_CHAT_KICKED = 13; 168 | USER_NOT_ALLOWED = 14; 169 | ENSURE_SPECIAL_PRIVILEGES_FAILED = 15; 170 | NEW_PLAYER_USER_NOT_ELIGIBLE = 16; 171 | SILENT_ERROR = 17; 172 | NEW_PLAYER_USER_BANNED = 18; 173 | } 174 | 175 | optional uint32 response = 1; 176 | optional string channel_name = 2; 177 | optional fixed64 channel_id = 3; 178 | optional uint32 max_members = 4; 179 | repeated dota.CMsgDOTAChatMember members = 5; 180 | optional dota.DOTAChatChannelType_t channel_type = 6 [default = DOTAChannelType_Regional]; 181 | optional dota.CMsgDOTAJoinChatChannelResponse.Result result = 7 [default = JOIN_SUCCESS]; 182 | optional bool gc_initiated_join = 8; 183 | optional uint32 channel_user_id = 9; 184 | optional string welcome_message = 10; 185 | optional dota.EChatSpecialPrivileges special_privileges = 11 [default = k_EChatSpecialPrivileges_None]; 186 | } 187 | 188 | message CMsgDOTAChatChannelFullUpdate { 189 | optional fixed64 channel_id = 1; 190 | repeated dota.CMsgDOTAChatMember members = 2; 191 | } 192 | 193 | message CMsgDOTAOtherJoinedChatChannel { 194 | optional fixed64 channel_id = 1; 195 | optional string persona_name = 2; 196 | optional fixed64 steam_id = 3; 197 | optional uint32 channel_user_id = 4; 198 | optional uint32 status = 5; 199 | } 200 | 201 | message CMsgDOTAOtherLeftChatChannel { 202 | optional fixed64 channel_id = 1; 203 | optional fixed64 steam_id = 2; 204 | optional uint32 channel_user_id = 3; 205 | } 206 | 207 | message CMsgDOTAChatChannelMemberUpdate { 208 | message JoinedMember { 209 | optional fixed64 steam_id = 1; 210 | optional string persona_name = 2; 211 | optional uint32 channel_user_id = 3; 212 | optional uint32 status = 4; 213 | } 214 | 215 | optional fixed64 channel_id = 1; 216 | repeated fixed64 left_steam_ids = 2; 217 | repeated dota.CMsgDOTAChatChannelMemberUpdate.JoinedMember joined_members = 3; 218 | } 219 | 220 | message CMsgDOTARequestChatChannelList { 221 | } 222 | 223 | message CMsgDOTARequestChatChannelListResponse { 224 | message ChatChannel { 225 | optional string channel_name = 1; 226 | optional uint32 num_members = 2; 227 | optional dota.DOTAChatChannelType_t channel_type = 3 [default = DOTAChannelType_Regional]; 228 | } 229 | 230 | repeated dota.CMsgDOTARequestChatChannelListResponse.ChatChannel channels = 1; 231 | } 232 | 233 | message CMsgDOTAChatGetUserList { 234 | optional fixed64 channel_id = 1; 235 | } 236 | 237 | message CMsgDOTAChatGetUserListResponse { 238 | message Member { 239 | optional fixed64 steam_id = 1; 240 | optional string persona_name = 2; 241 | optional uint32 channel_user_id = 3; 242 | optional uint32 status = 4; 243 | } 244 | 245 | optional fixed64 channel_id = 1; 246 | repeated dota.CMsgDOTAChatGetUserListResponse.Member members = 2; 247 | } 248 | 249 | message CMsgDOTAChatGetMemberCount { 250 | optional string channel_name = 1; 251 | optional dota.DOTAChatChannelType_t channel_type = 2 [default = DOTAChannelType_Regional]; 252 | } 253 | 254 | message CMsgDOTAChatGetMemberCountResponse { 255 | optional string channel_name = 1; 256 | optional dota.DOTAChatChannelType_t channel_type = 2 [default = DOTAChannelType_Regional]; 257 | optional uint32 member_count = 3; 258 | } 259 | 260 | message CMsgDOTAChatRegionsEnabled { 261 | message Region { 262 | optional float min_latitude = 1; 263 | optional float max_latitude = 2; 264 | optional float min_longitude = 3; 265 | optional float max_longitude = 4; 266 | } 267 | 268 | optional bool enable_all_regions = 1; 269 | repeated dota.CMsgDOTAChatRegionsEnabled.Region enabled_regions = 2; 270 | } 271 | -------------------------------------------------------------------------------- /protobufs/dota_gcmessages_client_team.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | import "dota_shared_enums.proto"; 4 | 5 | option optimize_for = SPEED; 6 | option py_generic_services = false; 7 | 8 | enum ETeamInviteResult { 9 | TEAM_INVITE_SUCCESS = 0; 10 | TEAM_INVITE_FAILURE_INVITE_REJECTED = 1; 11 | TEAM_INVITE_FAILURE_INVITE_TIMEOUT = 2; 12 | TEAM_INVITE_ERROR_TEAM_AT_MEMBER_LIMIT = 3; 13 | TEAM_INVITE_ERROR_TEAM_LOCKED = 4; 14 | TEAM_INVITE_ERROR_INVITEE_NOT_AVAILABLE = 5; 15 | TEAM_INVITE_ERROR_INVITEE_BUSY = 6; 16 | TEAM_INVITE_ERROR_INVITEE_ALREADY_MEMBER = 7; 17 | TEAM_INVITE_ERROR_INVITEE_AT_TEAM_LIMIT = 8; 18 | TEAM_INVITE_ERROR_INVITEE_INSUFFICIENT_PLAY_TIME = 9; 19 | TEAM_INVITE_ERROR_INVITER_INVALID_ACCOUNT_TYPE = 10; 20 | TEAM_INVITE_ERROR_INVITER_NOT_ADMIN = 11; 21 | TEAM_INVITE_ERROR_INCORRECT_USER_RESPONDED = 12; 22 | TEAM_INVITE_ERROR_UNSPECIFIED = 13; 23 | } 24 | 25 | message CMsgDOTATeamMemberSDO { 26 | optional uint32 account_id = 1; 27 | repeated uint32 team_ids = 2; 28 | optional uint32 profile_team_id = 3; 29 | } 30 | 31 | message CMsgDOTATeamAdminSDO { 32 | optional uint32 account_id = 1; 33 | repeated uint32 team_ids = 2; 34 | } 35 | 36 | message CMsgDOTATeamMember { 37 | optional uint32 account_id = 1; 38 | optional uint32 time_joined = 4; 39 | } 40 | 41 | message CMsgDOTATeam { 42 | repeated dota.CMsgDOTATeamMember members = 1; 43 | optional uint32 team_id = 2; 44 | optional string name = 3; 45 | optional string tag = 4; 46 | optional uint32 admin_id = 5; 47 | optional uint32 time_created = 6; 48 | optional bool disbanded = 7; 49 | optional uint32 wins = 8; 50 | optional uint32 losses = 9; 51 | optional uint32 rank = 10; 52 | optional uint32 calibration_games_remaining = 24; 53 | optional uint64 logo = 11; 54 | optional uint64 base_logo = 12; 55 | optional uint64 banner_logo = 13; 56 | optional uint64 sponsor_logo = 14; 57 | optional string country_code = 15; 58 | optional string url = 16; 59 | optional uint32 fullgamesplayed = 17; 60 | repeated uint32 leagues = 18; 61 | optional uint32 gamesplayed = 19; 62 | optional uint32 gamesplayedwithcurrentroster = 20; 63 | optional uint32 teammatchmakinggamesplayed = 21; 64 | optional uint32 lastplayedgametime = 22; 65 | optional uint32 lastrenametime = 23; 66 | repeated uint64 recent_match_ids = 25; 67 | repeated uint64 top_match_ids = 26; 68 | optional bool pickup_team = 27; 69 | } 70 | 71 | message CMsgDOTATeamInfo { 72 | message HeroStats { 73 | optional uint32 hero_id = 1; 74 | optional uint32 picks = 2; 75 | optional uint32 wins = 3; 76 | optional uint32 bans = 4; 77 | optional float avg_kills = 5; 78 | optional float avg_deaths = 6; 79 | optional float avg_assists = 7; 80 | optional float avg_gpm = 8; 81 | optional float avg_xpm = 9; 82 | } 83 | 84 | message MemberStats { 85 | optional uint32 account_id = 1; 86 | optional uint32 wins_with_team = 2; 87 | optional uint32 losses_with_team = 3; 88 | repeated dota.CMsgDOTATeamInfo.HeroStats top_heroes = 4; 89 | optional float avg_kills = 5; 90 | optional float avg_deaths = 6; 91 | optional float avg_assists = 7; 92 | } 93 | 94 | message TeamStats { 95 | repeated dota.CMsgDOTATeamInfo.HeroStats played_heroes = 1; 96 | optional float farming = 2; 97 | optional float fighting = 3; 98 | optional float versatility = 4; 99 | optional float avg_kills = 5; 100 | optional float avg_deaths = 6; 101 | optional float avg_duration = 7; 102 | } 103 | 104 | message DPCResult { 105 | optional uint32 league_id = 1; 106 | optional uint32 standing = 2; 107 | optional uint32 points = 3; 108 | optional uint32 earnings = 4; 109 | optional uint32 timestamp = 5; 110 | } 111 | 112 | message Member { 113 | optional uint32 account_id = 1; 114 | optional uint32 time_joined = 2; 115 | optional bool admin = 3; 116 | } 117 | 118 | message AuditEntry { 119 | optional uint32 audit_action = 1; 120 | optional uint32 timestamp = 2; 121 | optional uint32 account_id = 3; 122 | } 123 | 124 | repeated dota.CMsgDOTATeamInfo.Member members = 1; 125 | optional uint32 team_id = 2; 126 | optional string name = 3; 127 | optional string tag = 4; 128 | optional uint32 time_created = 5; 129 | optional bool pro = 6; 130 | optional bool pickup_team = 8; 131 | optional uint64 ugc_logo = 9; 132 | optional uint64 ugc_base_logo = 10; 133 | optional uint64 ugc_banner_logo = 11; 134 | optional uint64 ugc_sponsor_logo = 12; 135 | optional string country_code = 13; 136 | optional string url = 14; 137 | optional uint32 wins = 15; 138 | optional uint32 losses = 16; 139 | optional uint32 games_played_total = 19; 140 | optional uint32 games_played_matchmaking = 20; 141 | optional string url_logo = 24; 142 | repeated uint32 registered_member_account_ids = 30; 143 | optional uint32 coach_account_id = 36; 144 | repeated dota.CMsgDOTATeamInfo.AuditEntry audit_entries = 31; 145 | optional dota.ELeagueRegion region = 29 [default = LEAGUE_REGION_UNSET]; 146 | optional string abbreviation = 32; 147 | repeated dota.CMsgDOTATeamInfo.MemberStats member_stats = 33; 148 | optional dota.CMsgDOTATeamInfo.TeamStats team_stats = 34; 149 | repeated dota.CMsgDOTATeamInfo.DPCResult dpc_results = 35; 150 | optional string color_primary = 37; 151 | optional string color_secondary = 38; 152 | optional uint32 team_captain = 39; 153 | } 154 | 155 | message CMsgDOTATeamInfoRequest { 156 | optional dota.CMsgDOTATeamInfo result = 1; 157 | } 158 | 159 | message CMsgDOTATeamsInfo { 160 | optional uint32 league_id = 1; 161 | repeated dota.CMsgDOTATeamInfo teams = 2; 162 | } 163 | 164 | message CMsgDOTATeamInfoList { 165 | repeated dota.CMsgDOTATeamInfo teams = 1; 166 | } 167 | 168 | message CMsgDOTAMyTeamInfoRequest { 169 | } 170 | 171 | message CMsgDOTACreateTeam { 172 | optional string name = 1; 173 | optional string tag = 2; 174 | optional uint64 logo = 3; 175 | optional uint64 base_logo = 4; 176 | optional uint64 banner_logo = 5; 177 | optional uint64 sponsor_logo = 6; 178 | optional string country_code = 7; 179 | optional string url = 8; 180 | optional bool pickup_team = 9; 181 | optional string abbreviation = 10; 182 | } 183 | 184 | message CMsgDOTACreateTeamResponse { 185 | enum Result { 186 | INVALID = -1; 187 | SUCCESS = 0; 188 | NAME_EMPTY = 1; 189 | NAME_BAD_CHARACTERS = 2; 190 | NAME_TAKEN = 3; 191 | NAME_TOO_LONG = 4; 192 | TAG_EMPTY = 5; 193 | TAG_BAD_CHARACTERS = 6; 194 | TAG_TAKEN = 7; 195 | TAG_TOO_LONG = 8; 196 | CREATOR_BUSY = 9; 197 | UNSPECIFIED_ERROR = 10; 198 | CREATOR_TEAM_LIMIT_REACHED = 11; 199 | NO_LOGO = 12; 200 | CREATOR_TEAM_CREATION_COOLDOWN = 13; 201 | LOGO_UPLOAD_FAILED = 14; 202 | NAME_CHANGED_TOO_RECENTLY = 15; 203 | CREATOR_INSUFFICIENT_LEVEL = 16; 204 | INVALID_ACCOUNT_TYPE = 17; 205 | } 206 | 207 | optional dota.CMsgDOTACreateTeamResponse.Result result = 1 [default = INVALID]; 208 | optional uint32 team_id = 2; 209 | } 210 | 211 | message CMsgDOTAEditTeamDetails { 212 | optional uint32 team_id = 1; 213 | optional string name = 2; 214 | optional string tag = 3; 215 | optional uint64 logo = 4; 216 | optional uint64 base_logo = 5; 217 | optional uint64 banner_logo = 6; 218 | optional uint64 sponsor_logo = 7; 219 | optional string country_code = 8; 220 | optional string url = 9; 221 | optional bool in_use_by_party = 10; 222 | optional string abbreviation = 11; 223 | } 224 | 225 | message CMsgDOTAEditTeamDetailsResponse { 226 | enum Result { 227 | SUCCESS = 0; 228 | FAILURE_INVALID_ACCOUNT_TYPE = 1; 229 | FAILURE_NOT_MEMBER = 2; 230 | FAILURE_TEAM_LOCKED = 3; 231 | FAILURE_UNSPECIFIED_ERROR = 4; 232 | } 233 | 234 | optional dota.CMsgDOTAEditTeamDetailsResponse.Result result = 1 [default = SUCCESS]; 235 | } 236 | 237 | message CMsgDOTATeamProfileResponse { 238 | optional uint32 eresult = 1; 239 | optional dota.CMsgDOTATeam team = 2; 240 | } 241 | 242 | message CMsgDOTAProTeamListRequest { 243 | } 244 | 245 | message CMsgDOTAProTeamListResponse { 246 | message TeamEntry { 247 | optional uint32 team_id = 1; 248 | optional string tag = 2; 249 | optional uint32 time_created = 3; 250 | optional uint64 logo = 4; 251 | optional string country_code = 5; 252 | optional uint32 member_count = 6; 253 | } 254 | 255 | repeated dota.CMsgDOTAProTeamListResponse.TeamEntry teams = 1; 256 | optional uint32 eresult = 2; 257 | } 258 | 259 | message CMsgDOTATeamInvite_InviterToGC { 260 | optional uint32 account_id = 1; 261 | optional uint32 team_id = 2; 262 | } 263 | 264 | message CMsgDOTATeamInvite_GCImmediateResponseToInviter { 265 | optional dota.ETeamInviteResult result = 1 [default = TEAM_INVITE_SUCCESS]; 266 | optional string invitee_name = 2; 267 | optional uint32 required_play_time = 3; 268 | } 269 | 270 | message CMsgDOTATeamInvite_GCRequestToInvitee { 271 | optional uint32 inviter_account_id = 1; 272 | optional string team_name = 2; 273 | optional string team_tag = 3; 274 | optional uint64 logo = 4; 275 | } 276 | 277 | message CMsgDOTATeamInvite_InviteeResponseToGC { 278 | optional dota.ETeamInviteResult result = 1 [default = TEAM_INVITE_SUCCESS]; 279 | } 280 | 281 | message CMsgDOTATeamInvite_GCResponseToInviter { 282 | optional dota.ETeamInviteResult result = 1 [default = TEAM_INVITE_SUCCESS]; 283 | optional string invitee_name = 2; 284 | } 285 | 286 | message CMsgDOTATeamInvite_GCResponseToInvitee { 287 | optional dota.ETeamInviteResult result = 1 [default = TEAM_INVITE_SUCCESS]; 288 | optional string team_name = 2; 289 | } 290 | 291 | message CMsgDOTAKickTeamMember { 292 | optional uint32 account_id = 1; 293 | optional uint32 team_id = 2; 294 | } 295 | 296 | message CMsgDOTAKickTeamMemberResponse { 297 | enum Result { 298 | SUCCESS = 0; 299 | FAILURE_INVALID_ACCOUNT_TYPE = 1; 300 | FAILURE_KICKER_NOT_ADMIN = 2; 301 | FAILURE_KICKEE_NOT_MEMBER = 3; 302 | FAILURE_TEAM_LOCKED = 4; 303 | FAILURE_UNSPECIFIED_ERROR = 5; 304 | } 305 | 306 | optional dota.CMsgDOTAKickTeamMemberResponse.Result result = 1 [default = SUCCESS]; 307 | } 308 | 309 | message CMsgDOTATransferTeamAdmin { 310 | optional uint32 new_admin_account_id = 1; 311 | optional uint32 team_id = 2; 312 | } 313 | 314 | message CMsgDOTATransferTeamAdminResponse { 315 | enum Result { 316 | SUCCESS = 0; 317 | FAILURE_INVALID_ACCOUNT_TYPE = 1; 318 | FAILURE_NOT_ADMIN = 2; 319 | FAILURE_SAME_ACCOUNT = 3; 320 | FAILURE_NOT_MEMBER = 4; 321 | FAILURE_UNSPECIFIED_ERROR = 5; 322 | } 323 | 324 | optional dota.CMsgDOTATransferTeamAdminResponse.Result result = 1 [default = SUCCESS]; 325 | } 326 | 327 | message CMsgDOTALeaveTeam { 328 | optional uint32 team_id = 1; 329 | } 330 | 331 | message CMsgDOTALeaveTeamResponse { 332 | enum Result { 333 | SUCCESS = 0; 334 | FAILURE_NOT_MEMBER = 1; 335 | FAILURE_TEAM_LOCKED = 2; 336 | FAILURE_UNSPECIFIED_ERROR = 3; 337 | } 338 | 339 | optional dota.CMsgDOTALeaveTeamResponse.Result result = 1 [default = SUCCESS]; 340 | } 341 | 342 | message CMsgDOTABetaParticipation { 343 | optional uint32 access_rights = 1; 344 | } 345 | -------------------------------------------------------------------------------- /protobufs/dota_gcmessages_client_tournament.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | import "dota_client_enums.proto"; 4 | 5 | option optimize_for = SPEED; 6 | option py_generic_services = false; 7 | 8 | enum ETournamentEvent { 9 | k_ETournamentEvent_None = 0; 10 | k_ETournamentEvent_TournamentCreated = 1; 11 | k_ETournamentEvent_TournamentsMerged = 2; 12 | k_ETournamentEvent_GameOutcome = 3; 13 | k_ETournamentEvent_TeamGivenBye = 4; 14 | k_ETournamentEvent_TournamentCanceledByAdmin = 5; 15 | k_ETournamentEvent_TeamAbandoned = 6; 16 | k_ETournamentEvent_ScheduledGameStarted = 7; 17 | k_ETournamentEvent_Canceled = 8; 18 | k_ETournamentEvent_TeamParticipationTimedOut_EntryFeeRefund = 9; 19 | k_ETournamentEvent_TeamParticipationTimedOut_EntryFeeForfeit = 10; 20 | k_ETournamentEvent_TeamParticipationTimedOut_GrantedVictory = 11; 21 | } 22 | 23 | message CMsgDOTATournamentInfo { 24 | message PhaseGroup { 25 | optional uint32 group_id = 1; 26 | optional string group_name = 2; 27 | } 28 | 29 | message Phase { 30 | optional uint32 phase_id = 1; 31 | optional string phase_name = 2; 32 | optional uint32 type_id = 3; 33 | optional uint32 iterations = 4; 34 | optional uint32 min_start_time = 5; 35 | optional uint32 max_start_time = 6; 36 | repeated dota.CMsgDOTATournamentInfo.PhaseGroup group_list = 7; 37 | } 38 | 39 | message Team { 40 | optional uint32 team_id = 1; 41 | optional string name = 2; 42 | optional string tag = 3; 43 | optional uint64 team_logo = 4; 44 | optional bool eliminated = 5; 45 | } 46 | 47 | message UpcomingMatch { 48 | optional uint32 series_id = 1; 49 | optional uint32 team1_id = 2; 50 | optional uint32 team2_id = 3; 51 | optional uint32 bo = 4; 52 | optional string stage_name = 5; 53 | optional uint32 start_time = 6; 54 | optional string winner_stage = 7; 55 | optional string loser_stage = 8; 56 | optional string team1_tag = 9; 57 | optional string team2_tag = 10; 58 | optional string team1_prev_opponent_tag = 11; 59 | optional string team2_prev_opponent_tag = 12; 60 | optional uint64 team1_logo = 13; 61 | optional uint64 team2_logo = 14; 62 | optional uint64 team1_prev_opponent_logo = 15; 63 | optional uint64 team2_prev_opponent_logo = 16; 64 | optional uint32 team1_prev_opponent_id = 17; 65 | optional uint32 team2_prev_opponent_id = 18; 66 | optional uint32 team1_prev_match_score = 19; 67 | optional uint32 team1_prev_match_opponent_score = 20; 68 | optional uint32 team2_prev_match_score = 21; 69 | optional uint32 team2_prev_match_opponent_score = 22; 70 | optional uint32 phase_type = 23; 71 | optional uint32 team1_score = 24; 72 | optional uint32 team2_score = 25; 73 | optional uint32 phase_id = 26; 74 | } 75 | 76 | message News { 77 | optional string link = 1; 78 | optional string title = 2; 79 | optional string image = 3; 80 | optional uint32 timestamp = 4; 81 | } 82 | 83 | optional uint32 league_id = 1; 84 | repeated dota.CMsgDOTATournamentInfo.Phase phase_list = 2; 85 | repeated dota.CMsgDOTATournamentInfo.Team teams_list = 3; 86 | repeated dota.CMsgDOTATournamentInfo.UpcomingMatch upcoming_matches_list = 4; 87 | repeated dota.CMsgDOTATournamentInfo.News news_list = 5; 88 | } 89 | 90 | message CMsgRequestWeekendTourneySchedule { 91 | } 92 | 93 | message CMsgWeekendTourneySchedule { 94 | message Division { 95 | optional uint32 division_code = 1; 96 | optional uint32 time_window_open = 2; 97 | optional uint32 time_window_close = 3; 98 | optional uint32 time_window_open_next = 4; 99 | optional uint32 trophy_id = 5; 100 | optional bool free_weekend = 6; 101 | } 102 | 103 | repeated dota.CMsgWeekendTourneySchedule.Division divisions = 1; 104 | } 105 | 106 | message CMsgWeekendTourneyOpts { 107 | optional bool participating = 1; 108 | optional uint32 division_id = 2; 109 | optional uint32 buyin = 3; 110 | optional uint32 skill_level = 4; 111 | optional uint32 match_groups = 5; 112 | optional uint32 team_id = 6; 113 | optional string pickup_team_name = 7; 114 | optional uint64 pickup_team_logo = 8; 115 | } 116 | 117 | message CMsgWeekendTourneyLeave { 118 | } 119 | 120 | message CMsgDOTATournament { 121 | message Team { 122 | optional fixed64 team_gid = 1; 123 | optional uint32 node_or_state = 2; 124 | repeated uint32 players = 3 [packed = true]; 125 | repeated uint32 player_buyin = 9 [packed = true]; 126 | repeated uint32 player_skill_level = 10 [packed = true]; 127 | optional uint32 match_group_mask = 12; 128 | optional uint32 team_id = 4; 129 | optional string team_name = 5; 130 | optional uint64 team_base_logo = 7; 131 | optional uint64 team_ui_logo = 8; 132 | } 133 | 134 | message Game { 135 | optional uint32 node_idx = 1; 136 | optional fixed64 lobby_id = 2; 137 | optional uint64 match_id = 3; 138 | optional bool team_a_good = 4; 139 | optional dota.ETournamentGameState state = 5 [default = k_ETournamentGameState_Unknown]; 140 | optional uint32 start_time = 6; 141 | } 142 | 143 | message Node { 144 | optional uint32 node_id = 1; 145 | optional uint32 team_idx_a = 2; 146 | optional uint32 team_idx_b = 3; 147 | optional dota.ETournamentNodeState node_state = 4 [default = k_ETournamentNodeState_Unknown]; 148 | } 149 | 150 | optional uint32 tournament_id = 1; 151 | optional uint32 division_id = 2; 152 | optional uint32 schedule_time = 3; 153 | optional uint32 skill_level = 4; 154 | optional dota.ETournamentTemplate tournament_template = 5 [default = k_ETournamentTemplate_None]; 155 | optional dota.ETournamentState state = 6 [default = k_ETournamentState_Unknown]; 156 | optional uint32 state_seq_num = 10; 157 | optional uint32 season_trophy_id = 11; 158 | repeated dota.CMsgDOTATournament.Team teams = 7; 159 | repeated dota.CMsgDOTATournament.Game games = 8; 160 | repeated dota.CMsgDOTATournament.Node nodes = 9; 161 | } 162 | 163 | message CMsgDOTATournamentStateChange { 164 | message GameChange { 165 | optional uint64 match_id = 1; 166 | optional dota.ETournamentGameState new_state = 2 [default = k_ETournamentGameState_Unknown]; 167 | } 168 | 169 | message TeamChange { 170 | optional uint64 team_gid = 1; 171 | optional uint32 new_node_or_state = 2; 172 | optional uint32 old_node_or_state = 3; 173 | } 174 | 175 | optional uint32 new_tournament_id = 1; 176 | optional dota.ETournamentEvent event = 2 [default = k_ETournamentEvent_None]; 177 | optional dota.ETournamentState new_tournament_state = 3 [default = k_ETournamentState_Unknown]; 178 | repeated dota.CMsgDOTATournamentStateChange.GameChange game_changes = 4; 179 | repeated dota.CMsgDOTATournamentStateChange.TeamChange team_changes = 5; 180 | repeated uint32 merged_tournament_ids = 6 [packed = true]; 181 | optional uint32 state_seq_num = 7; 182 | } 183 | 184 | message CMsgDOTATournamentRequest { 185 | optional uint32 tournament_id = 1; 186 | optional uint64 client_tournament_gid = 2; 187 | } 188 | 189 | message CMsgDOTATournamentResponse { 190 | optional uint32 result = 1 [default = 2]; 191 | optional dota.CMsgDOTATournament tournament = 2; 192 | } 193 | 194 | message CMsgDOTAClearTournamentGame { 195 | optional uint32 tournament_id = 1; 196 | optional uint32 game_id = 2; 197 | } 198 | 199 | message CMsgDOTAWeekendTourneyPlayerSkillLevelStats { 200 | optional uint32 skill_level = 1; 201 | optional uint32 times_won_0 = 2; 202 | optional uint32 times_won_1 = 3; 203 | optional uint32 times_won_2 = 4; 204 | optional uint32 times_won_3 = 5; 205 | optional uint32 times_bye_and_lost = 6; 206 | optional uint32 times_bye_and_won = 7; 207 | optional uint32 times_unusual_champ = 10; 208 | optional uint32 total_games_won = 8; 209 | optional uint32 score = 9; 210 | } 211 | 212 | message CMsgDOTAWeekendTourneyPlayerStats { 213 | optional uint32 account_id = 1; 214 | optional uint32 season_trophy_id = 2; 215 | repeated dota.CMsgDOTAWeekendTourneyPlayerSkillLevelStats skill_levels = 3; 216 | optional uint32 current_tier = 4; 217 | } 218 | 219 | message CMsgDOTAWeekendTourneyPlayerStatsRequest { 220 | optional uint32 account_id = 1; 221 | optional uint32 season_trophy_id = 2; 222 | } 223 | 224 | message CMsgDOTAWeekendTourneyPlayerHistoryRequest { 225 | optional uint32 account_id = 1; 226 | optional uint32 season_trophy_id = 2; 227 | } 228 | 229 | message CMsgDOTAWeekendTourneyPlayerHistory { 230 | message Tournament { 231 | optional uint32 tournament_id = 1; 232 | optional uint32 start_time = 2; 233 | optional uint32 tournament_tier = 3; 234 | optional uint32 team_id = 4; 235 | optional uint32 team_date = 5; 236 | optional uint32 team_result = 6; 237 | repeated uint32 account_id = 7; 238 | optional string team_name = 8; 239 | optional uint32 season_trophy_id = 9; 240 | } 241 | 242 | optional uint32 account_id = 1; 243 | repeated dota.CMsgDOTAWeekendTourneyPlayerHistory.Tournament tournaments = 3; 244 | } 245 | 246 | message CMsgDOTAWeekendTourneyParticipationDetails { 247 | message Tier { 248 | optional uint32 tier = 1; 249 | optional uint32 players = 2; 250 | optional uint32 teams = 3; 251 | optional uint32 winning_teams = 4; 252 | optional uint32 players_streak_2 = 5; 253 | optional uint32 players_streak_3 = 6; 254 | optional uint32 players_streak_4 = 7; 255 | optional uint32 players_streak_5 = 8; 256 | } 257 | 258 | message Division { 259 | optional uint32 division_id = 1; 260 | optional uint32 schedule_time = 2; 261 | repeated dota.CMsgDOTAWeekendTourneyParticipationDetails.Tier tiers = 3; 262 | } 263 | 264 | repeated dota.CMsgDOTAWeekendTourneyParticipationDetails.Division divisions = 1; 265 | } 266 | -------------------------------------------------------------------------------- /protobufs/dota_gcmessages_client_watch.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | import "dota_gcmessages_common.proto"; 4 | 5 | option optimize_for = SPEED; 6 | option py_generic_services = false; 7 | 8 | message CSourceTVGameSmall { 9 | message Player { 10 | optional uint32 account_id = 1; 11 | optional uint32 hero_id = 2; 12 | } 13 | 14 | optional uint32 activate_time = 1; 15 | optional uint32 deactivate_time = 2; 16 | optional uint64 server_steam_id = 3; 17 | optional uint64 lobby_id = 4; 18 | optional uint32 league_id = 5; 19 | optional uint32 lobby_type = 6; 20 | optional int32 game_time = 7; 21 | optional uint32 delay = 8; 22 | optional uint32 spectators = 9; 23 | optional uint32 game_mode = 10; 24 | optional uint32 average_mmr = 11; 25 | optional uint64 match_id = 12; 26 | optional uint32 series_id = 13; 27 | optional string team_name_radiant = 15; 28 | optional string team_name_dire = 16; 29 | optional fixed64 team_logo_radiant = 24; 30 | optional fixed64 team_logo_dire = 25; 31 | optional uint32 team_id_radiant = 30; 32 | optional uint32 team_id_dire = 31; 33 | optional uint32 sort_score = 17; 34 | optional float last_update_time = 18; 35 | optional int32 radiant_lead = 19; 36 | optional uint32 radiant_score = 20; 37 | optional uint32 dire_score = 21; 38 | repeated dota.CSourceTVGameSmall.Player players = 22; 39 | optional fixed32 building_state = 23; 40 | optional uint32 weekend_tourney_tournament_id = 26; 41 | optional uint32 weekend_tourney_division = 27; 42 | optional uint32 weekend_tourney_skill_level = 28; 43 | optional uint32 weekend_tourney_bracket_round = 29; 44 | optional uint32 custom_game_difficulty = 32; 45 | } 46 | 47 | message CMsgClientToGCFindTopSourceTVGames { 48 | optional string search_key = 1; 49 | optional uint32 league_id = 2; 50 | optional uint32 hero_id = 3; 51 | optional uint32 start_game = 4; 52 | optional uint32 game_list_index = 5; 53 | repeated uint64 lobby_ids = 6; 54 | } 55 | 56 | message CMsgGCToClientFindTopSourceTVGamesResponse { 57 | optional string search_key = 1; 58 | optional uint32 league_id = 2; 59 | optional uint32 hero_id = 3; 60 | optional uint32 start_game = 4; 61 | optional uint32 num_games = 5; 62 | optional uint32 game_list_index = 6; 63 | repeated dota.CSourceTVGameSmall game_list = 7; 64 | optional bool specific_games = 8; 65 | optional dota.CSourceTVGameSmall bot_game = 9; 66 | } 67 | 68 | message CMsgGCToClientTopWeekendTourneyGames { 69 | repeated dota.CSourceTVGameSmall live_games = 1; 70 | } 71 | 72 | message CMsgClientToGCTopMatchesRequest { 73 | optional uint32 hero_id = 1; 74 | optional uint32 player_account_id = 2; 75 | optional uint32 team_id = 3; 76 | } 77 | 78 | message CMsgClientToGCTopLeagueMatchesRequest { 79 | } 80 | 81 | message CMsgClientToGCTopFriendMatchesRequest { 82 | } 83 | 84 | message CMsgClientToGCMatchesMinimalRequest { 85 | repeated uint64 match_ids = 1; 86 | } 87 | 88 | message CMsgClientToGCMatchesMinimalResponse { 89 | repeated dota.CMsgDOTAMatchMinimal matches = 1; 90 | optional bool last_match = 2; 91 | } 92 | 93 | message CMsgGCToClientTopLeagueMatchesResponse { 94 | repeated dota.CMsgDOTAMatchMinimal matches = 2; 95 | } 96 | 97 | message CMsgGCToClientTopFriendMatchesResponse { 98 | repeated dota.CMsgDOTAMatchMinimal matches = 1; 99 | } 100 | 101 | message CMsgClientToGCFindTopMatches { 102 | optional uint32 start_game = 1; 103 | optional uint32 league_id = 2; 104 | optional uint32 hero_id = 3; 105 | optional uint32 friend_id = 4; 106 | optional bool friend_list = 5; 107 | optional bool league_list = 6; 108 | } 109 | 110 | message CMsgGCToClientFindTopLeagueMatchesResponse { 111 | optional uint32 start_game = 1; 112 | optional uint32 league_id = 2; 113 | optional uint32 hero_id = 3; 114 | repeated uint32 match_ids = 4; 115 | repeated dota.CMsgDOTAMatch matches = 5; 116 | } 117 | 118 | message CMsgSpectateFriendGame { 119 | optional fixed64 steam_id = 1; 120 | optional bool live = 2; 121 | } 122 | 123 | message CMsgSpectateFriendGameResponse { 124 | enum EWatchLiveResult { 125 | SUCCESS = 0; 126 | ERROR_GENERIC = 1; 127 | ERROR_NO_PLUS = 2; 128 | ERROR_NOT_FRIENDS = 3; 129 | ERROR_LOBBY_NOT_FOUND = 4; 130 | ERROR_SPECTATOR_IN_A_LOBBY = 5; 131 | ERROR_LOBBY_IS_LAN = 6; 132 | ERROR_WRONG_LOBBY_TYPE = 7; 133 | ERROR_WRONG_LOBBY_STATE = 8; 134 | ERROR_PLAYER_NOT_PLAYER = 9; 135 | ERROR_TOO_MANY_SPECTATORS = 10; 136 | ERROR_SPECTATOR_SWITCHED_TEAMS = 11; 137 | ERROR_FRIENDS_ON_BOTH_SIDES = 12; 138 | ERROR_SPECTATOR_IN_THIS_LOBBY = 13; 139 | ERROR_LOBBY_IS_LEAGUE = 14; 140 | } 141 | 142 | optional fixed64 server_steamid = 4; 143 | optional dota.CMsgSpectateFriendGameResponse.EWatchLiveResult watch_live_result = 5 [default = SUCCESS]; 144 | } 145 | 146 | message CDOTAReplayDownloadInfo { 147 | message Highlight { 148 | optional uint32 timestamp = 1; 149 | optional string description = 2; 150 | } 151 | 152 | optional dota.CMsgDOTAMatchMinimal match = 1; 153 | optional string title = 2; 154 | optional string description = 3; 155 | optional uint32 size = 4; 156 | repeated string tags = 5; 157 | optional bool exists_on_disk = 6; 158 | } 159 | 160 | message CMsgWatchGame { 161 | optional fixed64 server_steamid = 1; 162 | optional uint32 client_version = 2; 163 | optional fixed64 watch_server_steamid = 3; 164 | optional uint64 lobby_id = 4; 165 | repeated uint32 regions = 5; 166 | } 167 | 168 | message CMsgCancelWatchGame { 169 | } 170 | 171 | message CMsgWatchGameResponse { 172 | enum WatchGameResult { 173 | PENDING = 0; 174 | READY = 1; 175 | GAMESERVERNOTFOUND = 2; 176 | UNAVAILABLE = 3; 177 | CANCELLED = 4; 178 | INCOMPATIBLEVERSION = 5; 179 | MISSINGLEAGUESUBSCRIPTION = 6; 180 | LOBBYNOTFOUND = 7; 181 | } 182 | 183 | optional dota.CMsgWatchGameResponse.WatchGameResult watch_game_result = 1 [default = PENDING]; 184 | optional uint32 source_tv_public_addr = 2; 185 | optional uint32 source_tv_private_addr = 3; 186 | optional uint32 source_tv_port = 4; 187 | optional fixed64 game_server_steamid = 5; 188 | optional fixed64 watch_server_steamid = 6; 189 | optional fixed64 watch_tv_unique_secret_code = 7; 190 | } 191 | 192 | message CMsgPartyLeaderWatchGamePrompt { 193 | optional fixed64 game_server_steamid = 5; 194 | } 195 | 196 | message CDOTABroadcasterInfo { 197 | optional uint32 account_id = 1; 198 | optional fixed64 server_steam_id = 2; 199 | optional bool live = 3; 200 | optional string team_name_radiant = 4; 201 | optional string team_name_dire = 5; 202 | optional uint32 series_game = 7; 203 | optional uint32 upcoming_broadcast_timestamp = 9; 204 | optional bool allow_live_video = 10; 205 | optional uint32 node_type = 11; 206 | optional string node_name = 12; 207 | } 208 | 209 | message CMsgDOTASeries { 210 | message TeamInfo { 211 | optional uint32 team_id = 1; 212 | optional string team_name = 2; 213 | optional string team_logo_url = 3; 214 | optional uint32 wager_count = 4; 215 | } 216 | 217 | message LiveGame { 218 | optional fixed64 server_steam_id = 1; 219 | optional dota.CMsgDOTASeries.TeamInfo team_radiant = 2; 220 | optional dota.CMsgDOTASeries.TeamInfo team_dire = 3; 221 | optional uint32 team_radiant_score = 4; 222 | optional uint32 team_dire_score = 5; 223 | } 224 | 225 | optional uint32 series_id = 1; 226 | optional uint32 series_type = 2; 227 | optional dota.CMsgDOTASeries.TeamInfo team_1 = 3; 228 | optional dota.CMsgDOTASeries.TeamInfo team_2 = 4; 229 | repeated dota.CMsgDOTAMatchMinimal match_minimal = 5; 230 | optional dota.CMsgDOTASeries.LiveGame live_game = 6; 231 | } 232 | -------------------------------------------------------------------------------- /protobufs/dota_match_metadata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | import "base_gcmessages.proto"; 4 | import "dota_gcmessages_common_match_management.proto"; 5 | import "dota_gcmessages_common.proto"; 6 | import "dota_shared_enums.proto"; 7 | 8 | option py_generic_services = false; 9 | 10 | message CDOTAMatchMetadataFile { 11 | required int32 version = 1; 12 | required uint64 match_id = 2; 13 | optional dota.CDOTAMatchMetadata metadata = 3; 14 | optional bytes private_metadata = 5; 15 | } 16 | 17 | message CDOTAMatchMetadata { 18 | message Team { 19 | message PlayerKill { 20 | optional uint32 victim_slot = 1; 21 | optional uint32 count = 2; 22 | } 23 | 24 | message ItemPurchase { 25 | optional uint32 item_id = 1; 26 | optional int32 purchase_time = 2; 27 | } 28 | 29 | message InventorySnapshot { 30 | repeated uint32 item_id = 1; 31 | optional int32 game_time = 2; 32 | optional uint32 kills = 3; 33 | optional uint32 deaths = 4; 34 | optional uint32 assists = 5; 35 | optional uint32 level = 6; 36 | } 37 | 38 | message AutoStyleCriteria { 39 | optional uint32 name_token = 1; 40 | optional float value = 2; 41 | } 42 | 43 | message StrangeGemProgress { 44 | optional uint32 kill_eater_type = 1; 45 | optional uint32 gem_item_def_index = 2; 46 | optional uint32 required_hero_id = 3; 47 | optional uint32 starting_value = 4; 48 | optional uint32 ending_value = 5; 49 | optional uint32 owner_item_def_index = 6; 50 | optional uint64 owner_item_id = 7; 51 | } 52 | 53 | message VictoryPrediction { 54 | optional uint64 item_id = 1; 55 | optional uint32 item_def_index = 2; 56 | optional uint32 starting_value = 3; 57 | optional bool is_victory = 4; 58 | } 59 | 60 | message SubChallenge { 61 | optional uint32 slot_id = 1; 62 | optional uint32 start_value = 2; 63 | optional uint32 end_value = 3; 64 | optional bool completed = 4; 65 | } 66 | 67 | message CavernChallengeResult { 68 | optional uint32 completed_path_id = 1; 69 | optional uint32 claimed_room_id = 2; 70 | } 71 | 72 | message ActionGrant { 73 | optional uint32 action_id = 1; 74 | optional uint32 quantity = 2; 75 | optional uint32 audit = 3; 76 | } 77 | 78 | message CandyGrant { 79 | optional uint32 points = 1; 80 | optional uint32 reason = 2; 81 | } 82 | 83 | message EventData { 84 | optional uint32 event_id = 1; 85 | optional uint32 event_points = 2; 86 | optional uint32 challenge_instance_id = 3; 87 | optional uint32 challenge_quest_id = 4; 88 | optional uint32 challenge_quest_challenge_id = 5; 89 | optional bool challenge_completed = 6; 90 | optional uint32 challenge_rank_completed = 7; 91 | optional uint32 challenge_rank_previously_completed = 8; 92 | optional bool event_owned = 9; 93 | repeated dota.CDOTAMatchMetadata.Team.SubChallenge sub_challenges_with_progress = 10; 94 | optional uint32 wager_winnings = 11; 95 | optional bool cavern_challenge_active = 12; 96 | optional uint32 cavern_challenge_winnings = 13; 97 | optional uint32 amount_wagered = 14; 98 | optional uint32 periodic_point_adjustments = 16; 99 | repeated dota.CDOTAMatchMetadata.Team.CavernChallengeResult cavern_challenge_map_results = 17; 100 | optional uint32 cavern_challenge_plus_shard_winnings = 18; 101 | repeated dota.CDOTAMatchMetadata.Team.ActionGrant actions_granted = 19; 102 | optional uint32 cavern_crawl_map_variant = 20; 103 | optional uint32 team_wager_bonus_pct = 21; 104 | optional uint32 wager_streak_pct = 22; 105 | repeated dota.CDOTAMatchMetadata.Team.CandyGrant candy_points_granted = 23; 106 | } 107 | 108 | message GauntletProgress { 109 | optional uint32 gauntlet_tier = 2; 110 | optional uint32 gauntlet_wins = 3; 111 | optional uint32 gauntlet_losses = 4; 112 | } 113 | 114 | message Player { 115 | message ContractProgress { 116 | optional uint32 guild_id = 1; 117 | optional uint32 event_id = 2; 118 | optional uint32 challenge_instance_id = 3; 119 | optional uint32 challenge_parameter = 4; 120 | optional uint32 contract_stars = 5; 121 | optional uint32 contract_slot = 6; 122 | optional bool completed = 7; 123 | } 124 | 125 | optional uint32 account_id = 1; 126 | repeated uint32 ability_upgrades = 2; 127 | optional uint32 player_slot = 3; 128 | repeated dota.CSOEconItem equipped_econ_items = 4; 129 | repeated dota.CDOTAMatchMetadata.Team.PlayerKill kills = 5; 130 | repeated dota.CDOTAMatchMetadata.Team.ItemPurchase items = 6; 131 | optional uint32 avg_kills_x16 = 7; 132 | optional uint32 avg_deaths_x16 = 8; 133 | optional uint32 avg_assists_x16 = 9; 134 | optional uint32 avg_gpm_x16 = 10; 135 | optional uint32 avg_xpm_x16 = 11; 136 | optional uint32 best_kills_x16 = 12; 137 | optional uint32 best_assists_x16 = 13; 138 | optional uint32 best_gpm_x16 = 14; 139 | optional uint32 best_xpm_x16 = 15; 140 | optional uint32 win_streak = 16; 141 | optional uint32 best_win_streak = 17; 142 | optional float fight_score = 18; 143 | optional float farm_score = 19; 144 | optional float support_score = 20; 145 | optional float push_score = 21; 146 | repeated uint32 level_up_times = 22; 147 | repeated float graph_net_worth = 23; 148 | repeated dota.CDOTAMatchMetadata.Team.InventorySnapshot inventory_snapshot = 24; 149 | optional bool avg_stats_calibrated = 25; 150 | repeated dota.CDOTAMatchMetadata.Team.AutoStyleCriteria auto_style_criteria = 26; 151 | repeated dota.CDOTAMatchMetadata.Team.EventData event_data = 29; 152 | repeated dota.CDOTAMatchMetadata.Team.StrangeGemProgress strange_gem_progress = 30; 153 | optional uint32 hero_xp = 31; 154 | optional uint32 camps_stacked = 32; 155 | repeated dota.CDOTAMatchMetadata.Team.VictoryPrediction victory_prediction = 33; 156 | optional uint32 lane_selection_flags = 34; 157 | optional uint32 rampages = 35; 158 | optional uint32 triple_kills = 36; 159 | optional uint32 aegis_snatched = 37; 160 | optional uint32 rapiers_purchased = 38; 161 | optional uint32 couriers_killed = 39; 162 | optional uint32 net_worth_rank = 40; 163 | optional uint32 support_gold_spent = 41; 164 | optional uint32 observer_wards_placed = 42; 165 | optional uint32 sentry_wards_placed = 43; 166 | optional uint32 wards_dewarded = 44; 167 | optional float stun_duration = 45; 168 | optional dota.EDOTAMMRBoostType rank_mmr_boost_type = 46 [default = k_EDOTAMMRBoostType_None]; 169 | optional dota.CDOTAMatchMetadata.Team.GauntletProgress gauntlet_progress = 47; 170 | repeated dota.CDOTAMatchMetadata.Team.Player.ContractProgress contract_progress = 48; 171 | repeated uint32 guild_ids = 49; 172 | } 173 | 174 | optional uint32 dota_team = 1; 175 | repeated dota.CDOTAMatchMetadata.Team.Player players = 2; 176 | repeated float graph_experience = 3; 177 | repeated float graph_gold_earned = 4; 178 | repeated float graph_net_worth = 5; 179 | optional bool cm_first_pick = 6; 180 | optional uint32 cm_captain_player_id = 7; 181 | repeated uint32 cm_bans = 8; 182 | repeated uint32 cm_picks = 9; 183 | optional uint32 cm_penalty = 10; 184 | } 185 | 186 | message GuildChallengeProgress { 187 | message IndividualProgress { 188 | optional uint32 account_id = 1; 189 | optional uint32 progress = 2; 190 | } 191 | 192 | optional uint32 guild_id = 1; 193 | optional dota.EEvent event_id = 2 [default = EVENT_ID_NONE]; 194 | optional uint32 challenge_instance_id = 3; 195 | optional uint32 challenge_parameter = 4; 196 | optional uint32 challenge_timestamp = 5; 197 | optional uint32 challenge_progress_at_start = 6; 198 | optional uint32 challenge_progress_accumulated = 7; 199 | repeated dota.CDOTAMatchMetadata.GuildChallengeProgress.IndividualProgress individual_progress = 8; 200 | } 201 | 202 | repeated dota.CDOTAMatchMetadata.Team teams = 1; 203 | repeated dota.CLobbyTimedRewardDetails item_rewards = 2; 204 | optional fixed64 lobby_id = 3; 205 | optional fixed64 report_until_time = 4; 206 | optional bytes event_game_custom_table = 5; 207 | optional uint32 primary_event_id = 6; 208 | repeated dota.CMsgMatchTips match_tips = 7; 209 | optional dota.CMsgMatchMatchmakingStats matchmaking_stats = 8; 210 | optional dota.CMvpData mvp_data = 9; 211 | repeated dota.CDOTAMatchMetadata.GuildChallengeProgress guild_challenge_progress = 10; 212 | } 213 | 214 | message CDOTAMatchPrivateMetadata { 215 | message StringName { 216 | optional uint32 id = 1; 217 | optional string name = 2; 218 | } 219 | 220 | message Team { 221 | message Player { 222 | message CombatSegment { 223 | message DamageByAbility { 224 | message ByHeroTarget { 225 | optional uint32 hero_id = 1; 226 | optional uint32 damage = 2; 227 | } 228 | 229 | optional uint32 source_unit_index = 3; 230 | optional uint32 ability_id = 1; 231 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player.CombatSegment.DamageByAbility.ByHeroTarget by_hero_targets = 2; 232 | } 233 | 234 | message HealingByAbility { 235 | message ByHeroTarget { 236 | optional uint32 hero_id = 1; 237 | optional uint32 healing = 2; 238 | } 239 | 240 | optional uint32 source_unit_index = 3; 241 | optional uint32 ability_id = 1; 242 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player.CombatSegment.HealingByAbility.ByHeroTarget by_hero_targets = 2; 243 | } 244 | 245 | optional int32 game_time = 1; 246 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player.CombatSegment.DamageByAbility damage_by_ability = 2; 247 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player.CombatSegment.HealingByAbility healing_by_ability = 3; 248 | } 249 | 250 | message BuffRecord { 251 | message ByHeroTarget { 252 | optional uint32 hero_id = 1; 253 | optional float elapsed_duration = 2; 254 | optional bool is_hidden = 3; 255 | } 256 | 257 | optional uint32 buff_ability_id = 1; 258 | optional string buff_modifier_name = 3; 259 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player.BuffRecord.ByHeroTarget by_hero_targets = 2; 260 | } 261 | 262 | message GoldReceived { 263 | optional uint32 creep = 1; 264 | optional uint32 heroes = 2; 265 | optional uint32 bounty_runes = 3; 266 | optional uint32 passive = 4; 267 | optional uint32 buildings = 5; 268 | optional uint32 abilities = 6; 269 | optional uint32 wards = 7; 270 | optional uint32 other = 8; 271 | } 272 | 273 | message XPReceived { 274 | optional uint32 creep = 1; 275 | optional uint32 heroes = 2; 276 | optional uint32 roshan = 3; 277 | optional uint32 tome_of_knowledge = 4; 278 | optional uint32 outpost = 5; 279 | optional uint32 other = 6; 280 | } 281 | 282 | optional uint32 account_id = 1; 283 | optional uint32 player_slot = 2; 284 | optional bytes position_stream = 3; 285 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player.CombatSegment combat_segments = 4; 286 | repeated string damage_unit_names = 5; 287 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player.BuffRecord buff_records = 6; 288 | repeated float graph_kills = 7; 289 | repeated float graph_deaths = 8; 290 | repeated float graph_assists = 9; 291 | repeated float graph_lasthits = 10; 292 | repeated float graph_denies = 11; 293 | optional dota.CDOTAMatchPrivateMetadata.Team.Player.GoldReceived gold_received = 12; 294 | optional dota.CDOTAMatchPrivateMetadata.Team.Player.XPReceived xp_received = 13; 295 | } 296 | 297 | message Building { 298 | optional string unit_name = 1; 299 | optional uint32 position_quant_x = 2; 300 | optional uint32 position_quant_y = 3; 301 | optional float death_time = 4; 302 | } 303 | 304 | optional uint32 dota_team = 1; 305 | repeated dota.CDOTAMatchPrivateMetadata.Team.Player players = 2; 306 | repeated dota.CDOTAMatchPrivateMetadata.Team.Building buildings = 3; 307 | } 308 | 309 | repeated dota.CDOTAMatchPrivateMetadata.Team teams = 1; 310 | repeated float graph_win_probability = 2; 311 | repeated dota.CDOTAMatchPrivateMetadata.StringName string_names = 3; 312 | } 313 | 314 | message CMsgDOTADPCMatch { 315 | optional dota.CMsgDOTAMatch match = 1; 316 | optional dota.CDOTAMatchMetadata metadata = 2; 317 | } 318 | -------------------------------------------------------------------------------- /protobufs/econ_shared_enums.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | option optimize_for = SPEED; 4 | option py_generic_services = false; 5 | 6 | enum EGCEconBaseMsg { 7 | k_EMsgGCGenericResult = 2579; 8 | } 9 | 10 | enum EGCMsgResponse { 11 | k_EGCMsgResponseOK = 0; 12 | k_EGCMsgResponseDenied = 1; 13 | k_EGCMsgResponseServerError = 2; 14 | k_EGCMsgResponseTimeout = 3; 15 | k_EGCMsgResponseInvalid = 4; 16 | k_EGCMsgResponseNoMatch = 5; 17 | k_EGCMsgResponseUnknownError = 6; 18 | k_EGCMsgResponseNotLoggedOn = 7; 19 | k_EGCMsgFailedToCreate = 8; 20 | } 21 | 22 | enum EGCPartnerRequestResponse { 23 | k_EPartnerRequestOK = 1; 24 | k_EPartnerRequestBadAccount = 2; 25 | k_EPartnerRequestNotLinked = 3; 26 | k_EPartnerRequestUnsupportedPartnerType = 4; 27 | } 28 | 29 | enum EGCMsgUseItemResponse { 30 | k_EGCMsgUseItemResponse_ItemUsed = 0; 31 | k_EGCMsgUseItemResponse_GiftNoOtherPlayers = 1; 32 | k_EGCMsgUseItemResponse_ServerError = 2; 33 | k_EGCMsgUseItemResponse_MiniGameAlreadyStarted = 3; 34 | k_EGCMsgUseItemResponse_ItemUsed_ItemsGranted = 4; 35 | k_EGCMsgUseItemResponse_DropRateBonusAlreadyGranted = 5; 36 | k_EGCMsgUseItemResponse_NotInLowPriorityPool = 6; 37 | k_EGCMsgUseItemResponse_NotHighEnoughLevel = 7; 38 | k_EGCMsgUseItemResponse_EventNotActive = 8; 39 | k_EGCMsgUseItemResponse_ItemUsed_EventPointsGranted = 9; 40 | k_EGCMsgUseItemResponse_MissingRequirement = 10; 41 | k_EGCMsgUseItemResponse_EmoticonUnlock_NoNew = 11; 42 | k_EGCMsgUseItemResponse_EmoticonUnlock_Complete = 12; 43 | k_EGCMsgUseItemResponse_ItemUsed_Compendium = 13; 44 | } 45 | 46 | message CMsgGenericResult { 47 | optional uint32 eresult = 1 [default = 2]; 48 | optional string debug_message = 2; 49 | } 50 | -------------------------------------------------------------------------------- /protobufs/gcsdk_gcmessages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | import "steammessages.proto"; 4 | 5 | option optimize_for = SPEED; 6 | option py_generic_services = false; 7 | 8 | enum ESourceEngine { 9 | k_ESE_Source1 = 0; 10 | k_ESE_Source2 = 1; 11 | } 12 | 13 | enum PartnerAccountType { 14 | PARTNER_NONE = 0; 15 | PARTNER_PERFECT_WORLD = 1; 16 | PARTNER_INVALID = 3; 17 | } 18 | 19 | enum GCConnectionStatus { 20 | GCConnectionStatus_HAVE_SESSION = 0; 21 | GCConnectionStatus_GC_GOING_DOWN = 1; 22 | GCConnectionStatus_NO_SESSION = 2; 23 | GCConnectionStatus_NO_SESSION_IN_LOGON_QUEUE = 3; 24 | GCConnectionStatus_NO_STEAM = 4; 25 | GCConnectionStatus_SUSPENDED = 5; 26 | GCConnectionStatus_STEAM_GOING_DOWN = 6; 27 | } 28 | 29 | message CExtraMsgBlock { 30 | optional uint32 msg_type = 1; 31 | optional bytes contents = 2; 32 | optional uint64 msg_key = 3; 33 | optional bool is_compressed = 4; 34 | } 35 | 36 | message CMsgGCAssertJobData { 37 | optional string message_type = 1; 38 | optional bytes message_data = 2; 39 | } 40 | 41 | message CMsgGCConCommand { 42 | optional string command = 1; 43 | } 44 | 45 | message CMsgSDOAssert { 46 | message Request { 47 | repeated uint64 key = 1; 48 | optional string requesting_job = 2; 49 | } 50 | 51 | optional int32 sdo_type = 1; 52 | repeated dota.CMsgSDOAssert.Request requests = 2; 53 | } 54 | 55 | message CMsgSHA1Digest { 56 | required fixed64 block1 = 1; 57 | required fixed64 block2 = 2; 58 | required fixed32 block3 = 3; 59 | } 60 | 61 | message CMsgSOIDOwner { 62 | optional uint32 type = 1; 63 | optional uint64 id = 2; 64 | } 65 | 66 | message CMsgSOSingleObject { 67 | optional int32 type_id = 2; 68 | optional bytes object_data = 3; 69 | optional fixed64 version = 4; 70 | optional dota.CMsgSOIDOwner owner_soid = 5; 71 | optional uint32 service_id = 6; 72 | } 73 | 74 | message CMsgSOMultipleObjects { 75 | message SingleObject { 76 | option (msgpool_soft_limit) = 256; 77 | option (msgpool_hard_limit) = 1024; 78 | 79 | optional int32 type_id = 1; 80 | optional bytes object_data = 2; 81 | } 82 | 83 | repeated dota.CMsgSOMultipleObjects.SingleObject objects_modified = 2; 84 | optional fixed64 version = 3; 85 | repeated dota.CMsgSOMultipleObjects.SingleObject objects_added = 4; 86 | repeated dota.CMsgSOMultipleObjects.SingleObject objects_removed = 5; 87 | optional dota.CMsgSOIDOwner owner_soid = 6; 88 | optional uint32 service_id = 7; 89 | } 90 | 91 | message CMsgSOCacheSubscribed { 92 | message SubscribedType { 93 | optional int32 type_id = 1; 94 | repeated bytes object_data = 2; 95 | } 96 | 97 | repeated dota.CMsgSOCacheSubscribed.SubscribedType objects = 2; 98 | optional fixed64 version = 3; 99 | optional dota.CMsgSOIDOwner owner_soid = 4; 100 | optional uint32 service_id = 5; 101 | repeated uint32 service_list = 6; 102 | optional fixed64 sync_version = 7; 103 | } 104 | 105 | message CMsgSOCacheSubscribedUpToDate { 106 | optional fixed64 version = 1; 107 | optional dota.CMsgSOIDOwner owner_soid = 2; 108 | optional uint32 service_id = 3; 109 | repeated uint32 service_list = 4; 110 | optional fixed64 sync_version = 5; 111 | } 112 | 113 | message CMsgSOCacheUnsubscribed { 114 | optional dota.CMsgSOIDOwner owner_soid = 2; 115 | } 116 | 117 | message CMsgSOCacheSubscriptionCheck { 118 | optional fixed64 version = 2; 119 | optional dota.CMsgSOIDOwner owner_soid = 3; 120 | optional uint32 service_id = 4; 121 | repeated uint32 service_list = 5; 122 | optional fixed64 sync_version = 6; 123 | } 124 | 125 | message CMsgSOCacheSubscriptionRefresh { 126 | optional dota.CMsgSOIDOwner owner_soid = 2; 127 | } 128 | 129 | message CMsgSOCacheVersion { 130 | optional fixed64 version = 1; 131 | } 132 | 133 | message CMsgGCMultiplexMessage { 134 | optional uint32 msgtype = 1; 135 | optional bytes payload = 2; 136 | repeated fixed64 steamids = 3; 137 | } 138 | 139 | message CMsgGCToGCSubGCStarting { 140 | optional uint32 dir_index = 1; 141 | } 142 | 143 | message CGCToGCMsgMasterAck { 144 | message Process { 145 | optional uint32 dir_index = 1; 146 | repeated uint32 type_instances = 2; 147 | } 148 | 149 | optional uint32 dir_index = 1; 150 | optional string machine_name = 3; 151 | optional string process_name = 4; 152 | repeated dota.CGCToGCMsgMasterAck.Process directory = 6; 153 | } 154 | 155 | message CGCToGCMsgMasterAck_Response { 156 | optional int32 eresult = 1 [default = 2]; 157 | } 158 | 159 | message CMsgGCToGCUniverseStartup { 160 | optional bool is_initial_startup = 1; 161 | } 162 | 163 | message CMsgGCToGCUniverseStartupResponse { 164 | optional int32 eresult = 1; 165 | } 166 | 167 | message CGCToGCMsgMasterStartupComplete { 168 | message GCInfo { 169 | optional uint32 dir_index = 1; 170 | optional string machine_name = 2; 171 | } 172 | 173 | repeated dota.CGCToGCMsgMasterStartupComplete.GCInfo gc_info = 1; 174 | } 175 | 176 | message CGCToGCMsgRouted { 177 | optional uint32 msg_type = 1; 178 | optional fixed64 sender_id = 2; 179 | optional bytes net_message = 3; 180 | } 181 | 182 | message CGCToGCMsgRoutedReply { 183 | optional uint32 msg_type = 1; 184 | optional bytes net_message = 2; 185 | } 186 | 187 | message CMsgGCUpdateSubGCSessionInfo { 188 | message CMsgUpdate { 189 | optional fixed64 steamid = 1; 190 | optional fixed32 ip = 2; 191 | optional bool trusted = 3; 192 | } 193 | 194 | repeated dota.CMsgGCUpdateSubGCSessionInfo.CMsgUpdate updates = 1; 195 | } 196 | 197 | message CMsgGCRequestSubGCSessionInfo { 198 | optional fixed64 steamid = 1; 199 | } 200 | 201 | message CMsgGCRequestSubGCSessionInfoResponse { 202 | optional fixed32 ip = 1; 203 | optional bool trusted = 2; 204 | optional uint32 port = 3; 205 | optional bool success = 4; 206 | } 207 | 208 | message CMsgSOCacheHaveVersion { 209 | optional dota.CMsgSOIDOwner soid = 1; 210 | optional fixed64 version = 2; 211 | optional uint32 service_id = 3; 212 | optional uint32 cached_file_version = 4; 213 | } 214 | 215 | message CMsgClientHello { 216 | optional uint32 version = 1; 217 | repeated dota.CMsgSOCacheHaveVersion socache_have_versions = 2; 218 | optional uint32 client_session_need = 3; 219 | optional dota.PartnerAccountType client_launcher = 4 [default = PARTNER_NONE]; 220 | optional string secret_key = 5; 221 | optional uint32 client_language = 6; 222 | optional dota.ESourceEngine engine = 7 [default = k_ESE_Source1]; 223 | optional bytes steamdatagram_login = 8; 224 | optional uint32 platform_id = 9; 225 | optional bytes game_msg = 10; 226 | optional int32 os_type = 11; 227 | optional uint32 render_system = 12; 228 | optional uint32 render_system_req = 13; 229 | optional uint32 screen_width = 14; 230 | optional uint32 screen_height = 15; 231 | optional uint32 screen_refresh = 16; 232 | optional uint32 render_width = 17; 233 | optional uint32 render_height = 18; 234 | optional uint32 swap_width = 19; 235 | optional uint32 swap_height = 20; 236 | optional bool is_steam_china = 22; 237 | optional string platform_name = 23; 238 | } 239 | 240 | message CMsgClientWelcome { 241 | message Location { 242 | optional float latitude = 1; 243 | optional float longitude = 2; 244 | optional string country = 3; 245 | } 246 | 247 | optional uint32 version = 1; 248 | optional bytes game_data = 2; 249 | repeated dota.CMsgSOCacheSubscribed outofdate_subscribed_caches = 3; 250 | repeated dota.CMsgSOCacheSubscriptionCheck uptodate_subscribed_caches = 4; 251 | optional dota.CMsgClientWelcome.Location location = 5; 252 | optional bytes save_game_key = 6; 253 | optional fixed32 item_schema_crc = 7; 254 | optional string items_game_url = 8; 255 | optional uint32 gc_socache_file_version = 9; 256 | optional string txn_country_code = 10; 257 | optional bytes game_data2 = 11; 258 | optional uint32 rtime32_gc_welcome_timestamp = 12; 259 | optional uint32 currency = 13; 260 | optional uint32 balance = 14; 261 | optional string balance_url = 15; 262 | optional bool has_accepted_china_ssa = 16; 263 | optional bool is_banned_steam_china = 17; 264 | optional dota.CExtraMsgBlock additional_welcome_msgs = 18; 265 | } 266 | 267 | message CMsgConnectionStatus { 268 | optional dota.GCConnectionStatus status = 1 [default = GCConnectionStatus_HAVE_SESSION]; 269 | optional uint32 client_session_need = 2; 270 | optional int32 queue_position = 3; 271 | optional int32 queue_size = 4; 272 | optional int32 wait_seconds = 5; 273 | optional int32 estimated_wait_seconds_remaining = 6; 274 | } 275 | 276 | message CMsgGCToGCSOCacheSubscribe { 277 | message CMsgHaveVersions { 278 | optional uint32 service_id = 1; 279 | optional uint64 version = 2; 280 | } 281 | 282 | optional fixed64 subscriber = 1; 283 | optional fixed64 subscribe_to_id = 2; 284 | optional fixed64 sync_version = 3; 285 | repeated dota.CMsgGCToGCSOCacheSubscribe.CMsgHaveVersions have_versions = 4; 286 | optional uint32 subscribe_to_type = 5; 287 | } 288 | 289 | message CMsgGCToGCSOCacheUnsubscribe { 290 | optional fixed64 subscriber = 1; 291 | optional fixed64 unsubscribe_from_id = 2; 292 | optional uint32 unsubscribe_from_type = 3; 293 | } 294 | 295 | message CMsgGCClientPing { 296 | } 297 | 298 | message CMsgGCToGCForwardAccountDetails { 299 | optional fixed64 steamid = 1; 300 | optional dota.CGCSystemMsg_GetAccountDetails_Response account_details = 2; 301 | optional uint32 age_seconds = 3; 302 | } 303 | 304 | message CMsgGCToGCLoadSessionSOCache { 305 | optional uint32 account_id = 1; 306 | optional dota.CMsgGCToGCForwardAccountDetails forward_account_details = 2; 307 | } 308 | 309 | message CMsgGCToGCLoadSessionSOCacheResponse { 310 | } 311 | 312 | message CMsgGCToGCUpdateSessionStats { 313 | optional uint32 user_sessions = 1; 314 | optional uint32 server_sessions = 2; 315 | optional bool in_logon_surge = 3; 316 | } 317 | 318 | message CMsgGCToClientRequestDropped { 319 | } 320 | 321 | message CWorkshop_PopulateItemDescriptions_Request { 322 | message SingleItemDescription { 323 | optional uint32 gameitemid = 1; 324 | optional string item_description = 2; 325 | } 326 | 327 | message ItemDescriptionsLanguageBlock { 328 | optional string language = 1; 329 | repeated dota.CWorkshop_PopulateItemDescriptions_Request.SingleItemDescription descriptions = 2; 330 | } 331 | 332 | optional uint32 appid = 1; 333 | repeated dota.CWorkshop_PopulateItemDescriptions_Request.ItemDescriptionsLanguageBlock languages = 2; 334 | } 335 | 336 | message CWorkshop_GetContributors_Request { 337 | optional uint32 appid = 1; 338 | optional uint32 gameitemid = 2; 339 | } 340 | 341 | message CWorkshop_GetContributors_Response { 342 | repeated fixed64 contributors = 1; 343 | } 344 | 345 | message CWorkshop_SetItemPaymentRules_Request { 346 | message WorkshopItemPaymentRule { 347 | optional uint64 workshop_file_id = 1; 348 | optional float revenue_percentage = 2; 349 | optional string rule_description = 3; 350 | optional uint32 rule_type = 4 [default = 1]; 351 | } 352 | 353 | message WorkshopDirectPaymentRule { 354 | optional uint64 workshop_file_id = 1; 355 | optional string rule_description = 2; 356 | } 357 | 358 | message PartnerItemPaymentRule { 359 | optional uint32 account_id = 1; 360 | optional float revenue_percentage = 2; 361 | optional string rule_description = 3; 362 | } 363 | 364 | optional uint32 appid = 1; 365 | optional uint32 gameitemid = 2; 366 | repeated dota.CWorkshop_SetItemPaymentRules_Request.WorkshopItemPaymentRule associated_workshop_files = 3; 367 | repeated dota.CWorkshop_SetItemPaymentRules_Request.PartnerItemPaymentRule partner_accounts = 4; 368 | optional bool validate_only = 5; 369 | optional bool make_workshop_files_subscribable = 6; 370 | optional dota.CWorkshop_SetItemPaymentRules_Request.WorkshopDirectPaymentRule associated_workshop_file_for_direct_payments = 7; 371 | } 372 | 373 | message CWorkshop_SetItemPaymentRules_Response { 374 | repeated string validation_errors = 1; 375 | } 376 | 377 | message CCommunity_ClanAnnouncementInfo { 378 | optional uint64 gid = 1; 379 | optional uint64 clanid = 2; 380 | optional uint64 posterid = 3; 381 | optional string headline = 4; 382 | optional uint32 posttime = 5; 383 | optional uint32 updatetime = 6; 384 | optional string body = 7; 385 | optional int32 commentcount = 8; 386 | repeated string tags = 9; 387 | optional int32 language = 10; 388 | optional bool hidden = 11; 389 | optional fixed64 forum_topic_id = 12; 390 | } 391 | 392 | message CCommunity_GetClanAnnouncements_Request { 393 | optional uint64 steamid = 1; 394 | optional uint32 offset = 2; 395 | optional uint32 count = 3; 396 | optional uint32 maxchars = 4; 397 | optional bool strip_html = 5; 398 | repeated string required_tags = 6; 399 | optional bool require_no_tags = 7; 400 | repeated uint32 language_preference = 8; 401 | optional bool hidden_only = 9; 402 | optional bool only_gid = 10; 403 | optional uint32 rtime_oldest_date = 11; 404 | optional bool include_hidden = 12; 405 | optional bool include_partner_events = 13; 406 | } 407 | 408 | message CCommunity_GetClanAnnouncements_Response { 409 | optional uint32 maxchars = 1; 410 | optional bool strip_html = 2; 411 | repeated dota.CCommunity_ClanAnnouncementInfo announcements = 3; 412 | } 413 | 414 | message CBroadcast_PostGameDataFrame_Request { 415 | optional uint32 appid = 1; 416 | optional fixed64 steamid = 2; 417 | optional fixed64 broadcast_id = 3; 418 | optional bytes frame_data = 4; 419 | } 420 | 421 | message CMsgSerializedSOCache { 422 | message TypeCache { 423 | optional uint32 type = 1; 424 | repeated bytes objects = 2; 425 | optional uint32 service_id = 3; 426 | } 427 | 428 | message Cache { 429 | message Version { 430 | optional uint32 service = 1; 431 | optional uint64 version = 2; 432 | } 433 | 434 | optional uint32 type = 1; 435 | optional uint64 id = 2; 436 | repeated dota.CMsgSerializedSOCache.Cache.Version versions = 3; 437 | repeated dota.CMsgSerializedSOCache.TypeCache type_caches = 4; 438 | } 439 | 440 | optional uint32 file_version = 1; 441 | repeated dota.CMsgSerializedSOCache.Cache caches = 2; 442 | optional uint32 gc_socache_file_version = 3; 443 | } 444 | 445 | message CMsgGCToClientPollConvarRequest { 446 | optional string convar_name = 1; 447 | optional uint32 poll_id = 2; 448 | } 449 | 450 | message CMsgGCToClientPollConvarResponse { 451 | optional uint32 poll_id = 1; 452 | optional string convar_value = 2; 453 | } 454 | 455 | message CGCMsgCompressedMsgToClient { 456 | optional uint32 msg_id = 1; 457 | optional bytes compressed_msg = 2; 458 | } 459 | 460 | message CMsgGCToGCMasterBroadcastMessage { 461 | optional uint32 users_per_second = 1; 462 | optional bool send_to_users = 2; 463 | optional bool send_to_servers = 3; 464 | optional uint32 msg_id = 4; 465 | optional bytes msg_data = 5; 466 | } 467 | 468 | message CMsgGCToGCMasterSubscribeToCache { 469 | optional uint32 soid_type = 1; 470 | optional fixed64 soid_id = 2; 471 | repeated uint32 account_ids = 3; 472 | repeated fixed64 steam_ids = 4; 473 | } 474 | 475 | message CMsgGCToGCMasterSubscribeToCacheResponse { 476 | } 477 | 478 | message CMsgGCToGCMasterSubscribeToCacheAsync { 479 | optional dota.CMsgGCToGCMasterSubscribeToCache subscribe_msg = 1; 480 | } 481 | 482 | message CMsgGCToGCMasterUnsubscribeFromCache { 483 | optional uint32 soid_type = 1; 484 | optional fixed64 soid_id = 2; 485 | repeated uint32 account_ids = 3; 486 | repeated fixed64 steam_ids = 4; 487 | } 488 | 489 | message CMsgGCToGCMasterDestroyCache { 490 | optional uint32 soid_type = 1; 491 | optional fixed64 soid_id = 2; 492 | } 493 | -------------------------------------------------------------------------------- /protobufs/gcsystemmsgs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package dota; 3 | option optimize_for = SPEED; 4 | option py_generic_services = false; 5 | 6 | enum ESOMsg { 7 | k_ESOMsg_Create = 21; 8 | k_ESOMsg_Update = 22; 9 | k_ESOMsg_Destroy = 23; 10 | k_ESOMsg_CacheSubscribed = 24; 11 | k_ESOMsg_CacheUnsubscribed = 25; 12 | k_ESOMsg_UpdateMultiple = 26; 13 | k_ESOMsg_CacheSubscriptionRefresh = 28; 14 | k_ESOMsg_CacheSubscribedUpToDate = 29; 15 | } 16 | 17 | enum EGCBaseClientMsg { 18 | k_EMsgGCPingRequest = 3001; 19 | k_EMsgGCPingResponse = 3002; 20 | k_EMsgGCToClientPollConvarRequest = 3003; 21 | k_EMsgGCToClientPollConvarResponse = 3004; 22 | k_EMsgGCCompressedMsgToClient = 3005; 23 | k_EMsgGCCompressedMsgToClient_Legacy = 523; 24 | k_EMsgGCToClientRequestDropped = 3006; 25 | k_EMsgGCClientWelcome = 4004; 26 | k_EMsgGCServerWelcome = 4005; 27 | k_EMsgGCClientHello = 4006; 28 | k_EMsgGCServerHello = 4007; 29 | k_EMsgGCClientConnectionStatus = 4009; 30 | k_EMsgGCServerConnectionStatus = 4010; 31 | } 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | enum34==1.1.2; python_version < '3.4' 2 | gevent>=1.3.0 3 | protobuf>=3.0.0 4 | steam[client]~=1.0 5 | gevent-eventemitter~=2.1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | from codecs import open 5 | from os import path 6 | import sys 7 | 8 | here = path.abspath(path.dirname(__file__)) 9 | with open(path.join(here, 'README.rst'), encoding='utf-8') as f: 10 | long_description = f.read()\ 11 | .replace('.io/en/latest/', '.io/en/stable/')\ 12 | .replace('?badge=latest', '?badge=stable')\ 13 | .replace('projects/dota2/badge/?version=latest', 'projects/dota2/badge/?version=stable') 14 | with open(path.join(here, 'dota2/__init__.py'), encoding='utf-8') as f: 15 | __version__ = f.readline().split('"')[1] 16 | 17 | install_requires = [ 18 | 'steam[client]~=1.0', 19 | 'gevent-eventemitter~=2.1', 20 | 'gevent>=1.3.0', 21 | 'protobuf>=3.0.0', 22 | ] 23 | 24 | if sys.version_info < (3, 4): 25 | install_requires.append('enum34>=1.0.4') 26 | 27 | setup( 28 | name='dota2', 29 | version=__version__, 30 | description='Module for interacting Dota2\'s Game Coordinator', 31 | long_description=long_description, 32 | url='https://github.com/ValvePython/dota2', 33 | author="Rossen Georgiev", 34 | author_email='rossen@rgp.io', 35 | license='MIT', 36 | classifiers=[ 37 | 'Development Status :: 4 - Beta', 38 | 'Intended Audience :: Developers', 39 | 'License :: OSI Approved :: MIT License', 40 | 'Topic :: Software Development :: Libraries :: Python Modules', 41 | 'Natural Language :: English', 42 | 'Operating System :: OS Independent', 43 | 'Programming Language :: Python :: 2.7', 44 | 'Programming Language :: Python :: 3.4', 45 | 'Programming Language :: Python :: 3.5', 46 | 'Programming Language :: Python :: 3.6', 47 | 'Programming Language :: Python :: 3.7', 48 | 'Programming Language :: Python :: 3.8', 49 | ], 50 | keywords='valve steam steamid api webapi', 51 | packages=['dota2'] + ['dota2.'+x for x in find_packages(where='dota2')], 52 | install_requires=install_requires, 53 | zip_safe=True, 54 | ) 55 | --------------------------------------------------------------------------------