├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── dcli.yaml ├── .gitignore ├── LICENSE.md ├── README.md ├── RELEASE.md ├── examples ├── README.md ├── dclisyncd.ps1 ├── mail_report ├── session ├── session.ps1 ├── status_notification └── status_notification.ps1 ├── images ├── dcliad.png ├── dcliad_sm.png ├── dcliad_x_sm.png ├── dcliah.png ├── dcliah_sm.png └── dcliah_x_sm.png ├── service ├── README.md ├── dcli_sync_manager.py └── dclisync.service ├── src ├── Cargo.lock ├── Cargo.toml ├── dcli │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── actitvity_store_schema.sql │ └── src │ │ ├── activitystoreinterface.rs │ │ ├── apiclient.rs │ │ ├── apiinterface.rs │ │ ├── apiutils.rs │ │ ├── character.rs │ │ ├── crucible.rs │ │ ├── cruciblestats.rs │ │ ├── emblem.rs │ │ ├── enums │ │ ├── character.rs │ │ ├── completionreason.rs │ │ ├── itemtype.rs │ │ ├── medaltier.rs │ │ ├── mod.rs │ │ ├── mode.rs │ │ ├── moment.rs │ │ ├── platform.rs │ │ ├── standing.rs │ │ ├── stat.rs │ │ └── weaponsort.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── manifest │ │ ├── definitions.rs │ │ └── mod.rs │ │ ├── manifestinterface.rs │ │ ├── output.rs │ │ ├── playeractivitiessummary.rs │ │ ├── response │ │ ├── activities.rs │ │ ├── character.rs │ │ ├── cr.rs │ │ ├── drs.rs │ │ ├── ggms.rs │ │ ├── gmd.rs │ │ ├── gpr.rs │ │ ├── manifest.rs │ │ ├── mod.rs │ │ ├── pgcr.rs │ │ ├── sdpr.rs │ │ ├── stats.rs │ │ └── utils.rs │ │ ├── statscontainer.rs │ │ └── utils.rs ├── dclia │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── dcliad │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── dcliah │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── dclif │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── classascii.rs │ │ └── main.rs ├── dclim │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── main.rs │ │ └── manifest_info.rs ├── dclistat │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── dclisync │ ├── Cargo.toml │ ├── README.md │ ├── service │ │ └── dclisync.service │ └── src │ │ └── main.rs ├── dclitime │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── datetimeformat.rs │ │ └── main.rs ├── rust-toolchain.toml ├── rustfmt.toml └── tell │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── lib.rs │ └── macros.rs ├── tests ├── README.md ├── runapps └── runapps.bat └── wiki └── running-on-osx ├── dialog-1.png ├── dialog-2.png └── dialog-3.png /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Report** 14 | Steps to reproduce the behavior: 15 | 1. Run command with '--verbose' flag 16 | 2. Copy output into bug report 17 | 3. Include app that was being run and time of day. 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Desktop (please complete the following information):** 23 | - Operating System (mac, windows, linux) and release version. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /.github/workflows/dcli.yaml: -------------------------------------------------------------------------------- 1 | name: dcli 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build-linux-musl: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Setup Config 10 | id: config 11 | run: | 12 | echo ::set-output name=SOURCE_TAG::${GITHUB_REF/refs\/tags\//} 13 | echo ::set-output name=TARGET_NAME::x86_64-unknown-linux-musl 14 | - uses: actions/checkout@master 15 | - name: Build 16 | uses: stevenleadbeater/rust-musl-builder@master 17 | with: 18 | args: /bin/bash -c "export DESTINY_API_KEY=${{ secrets.DESTINY_API_KEY }} && rustup toolchain install 1.65.0 && rustup target add x86_64-unknown-linux-musl --toolchain=1.65.0 && rustup override set 1.65.0 && cargo build --manifest-path=src/Cargo.toml --release --target=x86_64-unknown-linux-musl" 19 | - name: Process and Package 20 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 21 | env: 22 | SOURCE_TAG: ${{ steps.config.outputs.SOURCE_TAG }} 23 | TARGET_NAME: ${{ steps.config.outputs.TARGET_NAME }} 24 | run: echo SOURCE_TAG ${SOURCE_TAG} && echo TARGET_NAME ${TARGET_NAME} && ls -l src/target/ && cp src/target/${TARGET_NAME}/release/dclia . && strip dclia && cp src/target/${TARGET_NAME}/release/dcliad . && strip dcliad && cp src/target/${TARGET_NAME}/release/dclim . && strip dclim && cp src/target/${TARGET_NAME}/release/dclitime . && strip dclitime && cp src/target/${TARGET_NAME}/release/dcliah . && strip dcliah && cp src/target/${TARGET_NAME}/release/dclisync . && strip dclisync && cp src/target/${TARGET_NAME}/release/dclistat . && strip dclistat && zip -j dcli_${TARGET_NAME}_${SOURCE_TAG}.zip RELEASE.md README.md LICENSE.md dclia dcliad dclim dclitime dcliah dclisync dclistat 25 | 26 | - name: Release 27 | uses: softprops/action-gh-release@v1 28 | if: startsWith(github.ref, 'refs/tags/') 29 | with: 30 | files: | 31 | dcli_${{ steps.config.outputs.TARGET_NAME }}_${{ steps.config.outputs.SOURCE_TAG }}.zip 32 | RELEASE.md 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | build-windows-msvc: 37 | runs-on: windows-latest 38 | steps: 39 | #set some environement variables that we can use through the rest of our 40 | #action 41 | - name: Setup Config 42 | id: config 43 | run: | 44 | echo ::set-output name=SOURCE_TAG::${GITHUB_REF/refs\/tags\//} 45 | echo ::set-output name=TARGET_NAME::x86_64-pc-windows-msvc 46 | shell: bash 47 | 48 | - uses: actions/checkout@v2 49 | - uses: actions-rs/toolchain@v1 50 | with: 51 | toolchain: stable 52 | target: ${{ steps.config.outputs.TARGET_NAME }} 53 | override: true 54 | - name: Build 55 | uses: actions-rs/cargo@v1 56 | env: 57 | DESTINY_API_KEY: ${{ secrets.DESTINY_API_KEY }} 58 | RUSTFLAGS: "-C target-feature=+crt-static" 59 | with: 60 | use-cross: true 61 | command: build 62 | args: --manifest-path=src/Cargo.toml --release --target ${{ steps.config.outputs.TARGET_NAME }} 63 | - name: Process 64 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 65 | env: 66 | SOURCE_TAG: ${{ steps.config.outputs.SOURCE_TAG }} 67 | TARGET_NAME: ${{ steps.config.outputs.TARGET_NAME }} 68 | run: rustup.exe toolchain install 1.65.0 && rustup.exe override set 1.65.0 && echo SOURCE_TAG ${SOURCE_TAG} && cp src/target/${TARGET_NAME}/release/dclia.exe . && strip dclia.exe && cp src/target/${TARGET_NAME}/release/dcliad.exe . && strip dcliad.exe && cp src/target/${TARGET_NAME}/release/dclim.exe . && strip dclim.exe && cp src/target/${TARGET_NAME}/release/dclitime.exe . && strip dclitime.exe && cp src/target/${TARGET_NAME}/release/dcliah.exe . && strip dcliah.exe && cp src/target/${TARGET_NAME}/release/dclisync.exe . && strip dclisync.exe && cp src/target/${TARGET_NAME}/release/dclistat.exe . && strip dclistat.exe 69 | shell: bash 70 | - name: Package 71 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 72 | run: Compress-Archive -Path RELEASE.md, README.md, LICENSE.md, dclia.exe, dcliad.exe, dclim.exe, dclitime.exe, dcliah.exe, dclisync.exe, dclistat.exe -DestinationPath dcli_${{ steps.config.outputs.TARGET_NAME }}_${{ steps.config.outputs.SOURCE_TAG }}.zip -CompressionLevel Optimal 73 | - name: Release 74 | uses: softprops/action-gh-release@v1 75 | if: startsWith(github.ref, 'refs/tags/') 76 | with: 77 | files: | 78 | dcli_${{ steps.config.outputs.TARGET_NAME }}_${{ steps.config.outputs.SOURCE_TAG }}.zip 79 | RELEASE.md 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | 83 | build-mac-x86_64: 84 | runs-on: macos-11 85 | steps: 86 | #set some environement variables that we can use through the rest of our 87 | #action 88 | - name: Setup Config 89 | id: config 90 | run: | 91 | echo ::set-output name=SOURCE_TAG::${GITHUB_REF/refs\/tags\//} 92 | echo ::set-output name=TARGET_NAME::x86_64-apple-darwin 93 | 94 | - uses: actions/checkout@v2 95 | - uses: actions-rs/toolchain@v1 96 | with: 97 | toolchain: stable 98 | target: ${{ steps.config.outputs.TARGET_NAME }} 99 | override: true 100 | - name: Build 101 | uses: actions-rs/cargo@v1 102 | env: 103 | DESTINY_API_KEY: ${{ secrets.DESTINY_API_KEY }} 104 | with: 105 | use-cross: true 106 | command: build 107 | args: --manifest-path=src/Cargo.toml --release --target ${{ steps.config.outputs.TARGET_NAME }} 108 | - name: Process 109 | if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 110 | env: 111 | SOURCE_TAG: ${{ steps.config.outputs.SOURCE_TAG }} 112 | TARGET_NAME: ${{ steps.config.outputs.TARGET_NAME }} 113 | DESTINY_API_KEY: ${{ secrets.DESTINY_API_KEY }} 114 | run: rustup toolchain install 1.65.0 && rustup override set 1.65.0 && echo SOURCE_TAG ${SOURCE_TAG} && cp src/target/${TARGET_NAME}/release/dclia . && strip dclia && cp src/target/${TARGET_NAME}/release/dcliad . && strip dcliad && cp src/target/${TARGET_NAME}/release/dclim . && strip dclim && cp src/target/${TARGET_NAME}/release/dclitime . && strip dclitime && cp src/target/${TARGET_NAME}/release/dcliah . && strip dcliah && cp src/target/${TARGET_NAME}/release/dclisync . && strip dclisync && cp src/target/${TARGET_NAME}/release/dclistat . && strip dclistat && zip -j dcli_${TARGET_NAME}_${SOURCE_TAG}.zip RELEASE.md README.md LICENSE.md dclia dcliad dclim dclitime dcliah dclisync dclistat 115 | 116 | - name: Release 117 | uses: softprops/action-gh-release@v1 118 | if: startsWith(github.ref, 'refs/tags/') 119 | with: 120 | files: | 121 | dcli_${{ steps.config.outputs.TARGET_NAME }}_${{ steps.config.outputs.SOURCE_TAG }}.zip 122 | RELEASE.md 123 | env: 124 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 125 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | debug/ 6 | target/ 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | .DS_Store 11 | 12 | notes.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mike Chambers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dcli 2 | 3 | dcli (Destiny Command Line Interface) is a collection of utilities and apps that provide a command line interface (CLI) for viewing player stats and data from Destiny 2, using the [Destiny 2 API](https://github.com/Bungie-net/api). 4 | 5 | [![](images/dcliah_x_sm.png)](images/dcliah.png) 6 | [![](images/dcliad_x_sm.png)](images/dcliad.png) 7 | 8 | If you run into any issues, have any ideas, or just want to chat, please post in [issues](https://github.com/mikechambers/dcli/issues) or share on [Discord](https://discord.gg/2Y8bV2Mq3p) 9 | 10 | ## Apps 11 | 12 | ### Utilities 13 | 14 | | TOOL | DESCRIPTION | 15 | | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | 16 | | [dclim](https://github.com/mikechambers/dcli/tree/main/src/dclim) | Manages and syncs the remote Destiny 2 API manifest database | 17 | | [dclisync](https://github.com/mikechambers/dcli/tree/main/src/dclisync) | Downloads and syncs Destiny 2 Crucible activity history into a local sqlite3 database file | 18 | | [dclitime](https://github.com/mikechambers/dcli/tree/main/src/dclitime) | Generates date / time stamps for Destiny 2 weekly event moments | 19 | 20 | ### Apps 21 | 22 | | TOOL | DESCRIPTION | 23 | | ----------------------------------------------------------------------- | ------------------------------------------------------------------ | 24 | | [dclia](https://github.com/mikechambers/dcli/tree/main/src/dclia) | Displays information on player's current activity within Destiny 2 | 25 | | [dcliah](https://github.com/mikechambers/dcli/tree/main/src/dcliah) | Displays Destiny 2 activity history and stats | 26 | | [dcliad](https://github.com/mikechambers/dcli/tree/main/src/dcliad) | Displays Destiny 2 Crucible activity / match details | 27 | | [dclistat](https://github.com/mikechambers/dcli/tree/main/src/dclistat) | Displays specified Destiny 2 PVP stats | 28 | 29 | ### Libraries 30 | 31 | | TOOL | DESCRIPTION | 32 | | --------------------------------------------------------------- | ------------------------------------------------------ | 33 | | [dcli](https://github.com/mikechambers/dcli/tree/main/src/dcli) | Library used across all of the dcli apps | 34 | | [tell](https://github.com/mikechambers/dcli/tree/main/src/tell) | Library used to manage output for console applications | 35 | 36 | Each tool page contains additional tool specific information and usage examples. 37 | 38 | You can also find some additional examples in the [examples](examples/) folder. 39 | 40 | ## Download and Installation 41 | 42 | You can download the latest binaries for Windows, Linux and x86_64 Mac from the [releases](https://github.com/mikechambers/dcli/releases/latest) page. 43 | 44 | Just download, place them in your path and run from the command line (use --help to get a list of options). You can find a script [here](https://github.com/mikechambers/dcli/blob/main/tests/) that will run all of the apps to verify they are working and in your path. 45 | 46 | [![](https://img.shields.io/github/v/release/mikechambers/dcli?style=social)](https://github.com/mikechambers/dcli/releases/latest) 47 | 48 | **IMPORTANT**: Mac binaries are not signed, which can cause some hassle the first time you run them. You can find info on how to easily run them [here](https://github.com/mikechambers/dcli/wiki/Running-dcli-tools-on-Mac-OS-X). 49 | 50 | ## Getting Started 51 | 52 | The core idea behind the project is to provide small, focused utilities that provide useful info by themselves, but that can also be combined together, or with other shell scripts to create greater functionality. 53 | 54 | To get started, download the release (or compile from source), and place the executables somewhere within your path so you can call them from anywhere. Doing this will make it easy to call from anywhere on your system and from other scripts. 55 | 56 | If you are running on Mac, make sure to [read this article](https://github.com/mikechambers/dcli/wiki/Running-dcli-tools-on-Mac-OS-X) to ensure everything will run correctly. 57 | 58 | Before viewing your stats you need to first sync the manifest (which contains information about weapons, maps, etc...). 59 | 60 | ### Download the manifest 61 | 62 | You can download the latest Destiny 2 manifest database using dclim. This contains information about all of the items and activities in Destiny 2, and is updated periodically. 63 | 64 | Just run: 65 | 66 | ``` 67 | $ dclim 68 | ``` 69 | 70 | and the manifest file will be downloaded and saved in a system appropriate directory. You should periodically run this command to check whether the manifest has been updated by Bungie. 71 | 72 | ### Sync your activities 73 | 74 | Next, lets sync all of our activity history to a local database and view data. This data will be used by other apps, such as dcliah to generate and display stats. 75 | 76 | ``` 77 | $ dclisync --add mesh#3230 78 | $ dclisync --sync 79 | ``` 80 | 81 | Replacing mesh#3230 with your own Bungie name. 82 | 83 | You can find your Bungie name in game, or on Bungie's site at [https://www.bungie.net/7/en/User/Account/IdentitySettings](https://www.bungie.net/7/en/User/Account/IdentitySettings). 84 | 85 | The first time you run this, it may take a couple of minutes to load all of your data (depending on the number of activities that you have). If any errors occur while syncing, just re-run the app when its done. It is smart enough to only sync the activities that it missed the first time. 86 | 87 | Once you have done the initial sync, subsequent activity syncs should be very fast. You can periodically re-run the app to keep things in sync, or have dcliah / dcliad automatically sync before it displays your stats. 88 | 89 | ### Grabbing data 90 | 91 | Lets see all of our Crucible stats since the weekly reset on Tuesday for our last played character: 92 | 93 | ``` 94 | $ dcliah --name mesh#3230 --moment weekly 95 | ``` 96 | 97 | Let's view our historic Crucible stats across all of our characters for all time: 98 | 99 | ``` 100 | $ dcliah --name mesh#3230 --mode all_pvp --moment all_time --class all 101 | ``` 102 | 103 | ### Putting it all together 104 | 105 | These can be useful on their own, but where they can be really powerful is when you start to customize them for how you want to use them. 106 | 107 | There are a couple of examples in the [examples directory](https://github.com/mikechambers/dcli/tree/main/examples): 108 | 109 | - Send a notification when you load into a new activity (particularly useful when playing crucible so you can see which map you are loading into) 110 | - Automatically generate weekly reports on your Crucible stats and email them to yourself 111 | - Track your Crucible stats per game play session 112 | 113 | ### Learning More 114 | 115 | At anytime, you can see which arguments and options are available by passing the _--help_ argument: 116 | 117 | ``` 118 | $ dcliah --help 119 | ``` 120 | 121 | You can also find additional documentation and examples on the [individual app pages for each app](https://github.com/mikechambers/dcli). 122 | 123 | ## Questions, Feature Requests, Feedback 124 | 125 | If you have any questions, feature requests, need help, or just want to chat, join the [dcli Discord server](https://discord.gg/2Y8bV2Mq3p). 126 | 127 | You can also log bugs and features requests on the [issues page](https://github.com/mikechambers/dcli/issues). 128 | 129 | ### Environment Variables 130 | 131 | #### DCLI_FIX_DATA 132 | 133 | If the `DCLI_FIX_DATA` environment variable is set to `TRUE` then when corrupt or missing data is returned from the Bungie API, and there is not a valid local version, DCLI will attempt to retrieve updated, non-corrupt data from Bungie. (This sometimes happens if a lot of people leave a game, and no player names will be returned from the server). 134 | 135 | Setting this to true can significantly slow down sync time, especially the initial sync, and in general, is meant to be used when using DCLI to create datastores for larger applications. 136 | 137 | #### RUST_LOG 138 | 139 | All dcli apps have support for log output via the [env_logger](https://docs.rs/env_logger/0.9.3/env_logger/) library. This is mostly used for development, but may be helpful when trying to debug any issues. 140 | 141 | ## Compiling 142 | 143 | Tools are written and compiled in [Rust](https://www.rust-lang.org/). The 1.50.0 rust version is required, which can be installed and set via: 144 | 145 | ``` 146 | $ rustup toolchain install 1.50.0 147 | $ rustup override set 1.50.0 148 | ``` 149 | 150 | ![Build Status](https://github.com/mikechambers/dcli/workflows/dcli/badge.svg) 151 | 152 | When compiling you must have an environment variable named `DESTINY_API_KEY` which contains your [Bungie API key](https://www.bungie.net/en/Application). 153 | 154 | To compile, switch to the `src/` directory and run: 155 | 156 | ``` 157 | $ cargo build --release 158 | ``` 159 | 160 | which will place the compiled tools in _src/target/release_ 161 | 162 | ## Privacy 163 | 164 | Note, in order for dclisync to be able to sync your data, you must have the following privacy options selected on your Bungie account at [https://www.bungie.net/7/en/User/Account/Privacy](https://www.bungie.net/7/en/User/Account/Privacy) 165 | 166 | - Show my Destiny game Activity feed on Bungie.net 167 | 168 | ## Known Issues 169 | 170 | - Tied matches are not displayed correctly, and are treated as a Victory. 171 | 172 | ## License 173 | 174 | Project released under a [MIT License](LICENSE.md). 175 | 176 | [![License: MIT](https://img.shields.io/badge/License-MIT-orange.svg)](LICENSE.md) 177 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # dcli examples 2 | 3 | This folder contains scripts and examples that demonstrate using the data from the dcli apps. 4 | 5 | If you have examples you would like to share you can: 6 | * Submit them via the [issues page](https://github.com/mikechambers/dcli/issues) 7 | * Submit them via a pull request 8 | * Share a link to a page or repository with the example 9 | 10 | Please provide a summary including any requirements (such as OS or shell environment), and make sure to comment your script. 11 | 12 | All examples hosted in this project are released under an MIT license. 13 | 14 | ## Examples 15 | 16 | ### session 17 | 18 | * [session for Bash](session) 19 | * [session.ps1 for PowerShell](session.ps1) 20 | 21 | Bash script (tested on OS X and Linux) that tracks and displays Crucible activity stats per play session. 22 | 23 | To use, just start the script when you start playing, and it will update your aggregate stats for your session in realtime. 24 | 25 | The script pulls member-id, platform and manifest-path from environment variables (see script for var names). You can also just directly edit the script and add them. 26 | 27 | Uses dclitime and dcliah. 28 | 29 | ### status_notification 30 | 31 | * [status_notification for Bash](status_notification) 32 | * [status_notification.ps1 for PowerShell](status_notification.ps1) 33 | 34 | Mac OS X Bash and Windows Powershell scripts script which monitors a player's Destiny 2 status, and sends a notification with info on the new status when it changes. 35 | 36 | This is particularly useful on Windows when playing Crucible, as it will display a notification as you load into the map, telling you which map you are loading into. 37 | 38 | ### mail_report 39 | 40 | Bash script that uses [dcliah](https://github.com/mikechambers/dcli/tree/main/src/dcliah) to generate and send an email report of weekly Crucible stats. Can be scheduled as part of a crontab job to automate sending. 41 | 42 | Requires that a sendmail client is [configured](https://blog.travismclarke.com/post/send-email-from-terminal/) on system (although that should be easy to change in the scripts). Requires a newer version of Bash, so you may need to upgrade if running on OS X. Read script for configuration info. 43 | 44 | ### Snippets 45 | 46 | #### Query the activity database for most kills in a single game 47 | 48 | ``` 49 | $ sqlite3 '/home/mesh/.local/share/dcli/dcli.sqlite3' 'select max(kills) as kills from character_activity_stats' 50 | ``` 51 | 52 | Outputs: 53 | 54 | ``` 55 | 33.0 56 | ``` 57 | -------------------------------------------------------------------------------- /examples/dclisyncd.ps1: -------------------------------------------------------------------------------- 1 | 2 | $check_interval_seconds = 30 3 | 4 | 5 | ############# program ############# 6 | 7 | while ($true) { 8 | 9 | $output = (dclisync --sync) 10 | 11 | Write-Output $output 12 | 13 | Start-Sleep -Seconds $check_interval_seconds 14 | } -------------------------------------------------------------------------------- /examples/mail_report: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Generates weekly PVP reports for Destiny 2, and emails them 4 | # 5 | # Created by Mike Chambers 6 | # https://www.mikechambers.com 7 | # 8 | # Released under an MIT License 9 | # More info at: 10 | # https://github.com/mikechambers/dcli/ 11 | # 12 | # This is a pretty simple script / output. 13 | # Requires dcliah v0.8.0 14 | 15 | # (pulling from environment but you can also hardcode) 16 | EMAIL_ADDRESS=$EMAIL 17 | 18 | # you can get member_id and platform from dclim 19 | # (here it pulls from environment but you can also hardcode) 20 | MEMBER_ID=$MEMBER_ID 21 | PLATFORM=$PLATFORM 22 | 23 | # modes to generate a report for. options include: all, control, 24 | # clash, mayhem, ironbanner, private, rumble, comp, quickplay 25 | # and trialsofosiris 26 | declare -a MODES=("all_pvp" "quickplay" "iron_banner" "trials_of_osiris" "rumble" "pvp_competitive" ) 27 | 28 | REPORT_START=$(date +"%A, %B %-d, %Y") 29 | REPORT_END=$(date +"%A, %B %-d, %Y") 30 | 31 | SUBJECT="Destiny Crucible Report for week of ${REPORT_START}" 32 | 33 | OUTPUT="Here is your weekly Destiny 2 Crucible report for the week ending on ${REPORT_END}." 34 | OUTPUT+="\n" 35 | OUTPUT+="\n" 36 | 37 | echo "Gathering data for report" 38 | 39 | # Iterate the string array using for loop 40 | for mode in "${MODES[@]}"; do 41 | 42 | echo "Getting data for $mode" 43 | 44 | PRETTY_MODE="" 45 | if [ "${mode}" == "all_pvp" ] 46 | then 47 | PRETTY_MODE="All Matches" 48 | elif [ "${mode}" == "quickplay" ] 49 | then 50 | PRETTY_MODE="Quickplay" 51 | elif [ "${mode}" == "iron_banner" ] 52 | then 53 | PRETTY_MODE="Iron Banner" 54 | elif [ "${mode}" == "trials_of_osiris" ] 55 | then 56 | PRETTY_MODE="Trials of Osiris" 57 | elif [ "${mode}" == "rumble" ] 58 | then 59 | PRETTY_MODE="Rumble" 60 | elif [ "${mode}" == "pvp_competitive" ] 61 | then 62 | PRETTY_MODE="Competitive" 63 | fi 64 | 65 | OUTPUT+=" ${PRETTY_MODE}\n" 66 | OUTPUT+="************************************************************************************************************\n" 67 | 68 | #2>&1 this redirects stderr to stdout so we can capture 69 | TMP_OUTPUT=$(dcliah --member-id "$MEMBER_ID" --platform "$PLATFORM" --class all --mode "$mode" --moment week --activity-limit 0 --weapon-count 10 2> /dev/null) 70 | 71 | if [ $? -eq 1 ] 72 | then 73 | echo "${TMP_OUTPUT}">&2 #redirect to stderr 74 | echo -e "\nError running dcliah (see output above). Aborting. Exit code received from command [$?]">&2 #redirect to stderr 75 | exit 1 76 | fi 77 | 78 | if [ "${TMP_OUTPUT}" == "No activities found" ] 79 | then 80 | OUTPUT+="No games played" 81 | OUTPUT+="\n" 82 | OUTPUT+="\n" 83 | else 84 | OUTPUT+="${TMP_OUTPUT}" 85 | OUTPUT+="\n" 86 | OUTPUT+="\n" 87 | OUTPUT+="\n" 88 | fi 89 | 90 | sleep 2 91 | done 92 | 93 | OUTPUT+="\n\n" 94 | OUTPUT+="Report generated using dcli\n" 95 | OUTPUT+="https://github.com/mikechambers/dcli" 96 | 97 | #echo -e # | mail -s "${SUBJECT}" $EMAIL_ADDRESS 98 | 99 | echo -e "${OUTPUT}\n" 100 | 101 | echo "Emailing report to: ${EMAIL_ADDRESS}" 102 | 103 | #just wrap in
, so it maintains formatting from dcliah output
104 | BODY="Mime-Version: 1.0\nContent-Type: text/html\n
${OUTPUT}
" 105 | 106 | (echo "To: ${EMAIL_ADDRESS}" 107 | echo "Subject: ${SUBJECT}" 108 | echo "Content-Type: text/html" 109 | echo -e "${BODY}" 110 | ) | sendmail -t 111 | -------------------------------------------------------------------------------- /examples/session: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Tracks and displays combined crucible stats per gameplay session. 4 | # 5 | # Created by Mike Chambers 6 | # https://www.mikechambers.com 7 | # 8 | # Released under an MIT License 9 | # More info at: 10 | # https://github.com/mikechambers/dcli/ 11 | # 12 | # Requires dcliah and dclitime v0.2.0 13 | 14 | #https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ 15 | set -u 16 | 17 | #pull variables from environment variables. Otherwise, you can 18 | #just manually set them below 19 | 20 | #can get from dclis 21 | MEMBER_ID=$MEMBER_ID 22 | 23 | #can get from dclis 24 | PLATFORM=$PLATFORM 25 | 26 | #how often we check (seconds) 27 | CHECK_INTERVAL=30 28 | 29 | #the mode to pull stats for 30 | MODE="all_pvp" 31 | 32 | #to moment to start pull stats from. 33 | MOMENT="now" 34 | #tip to track trials for the weekend MODE=trials_of_osiris and MOMENT=weekend 35 | 36 | #lets get the start time string 37 | SESSION_START=$(dclitime --moment ${MOMENT}) 38 | 39 | 40 | clear 41 | 42 | LAST_CHECK_WAS_ERROR=0 43 | #now just loop and keep checking the stats 44 | while : 45 | do 46 | 47 | #this redirects stderr put to /dev/null 48 | ACTIVITY_HISTORY=$(dcliah --member-id "${MEMBER_ID}" --platform "${PLATFORM}" --mode "${MODE}" --moment custom --custom-time "${SESSION_START}" 2> /dev/null) 49 | 50 | #check and see if an error occured. 51 | if [ $? -eq 1 ] 52 | then 53 | if [ $LAST_CHECK_WAS_ERROR -eq 0 ] ; then 54 | echo -e "\nError retrieving activities. Trying again in ${CHECK_INTERVAL} seconds" 55 | LAST_CHECK_WAS_ERROR=1 56 | fi 57 | else 58 | LAST_CHECK_WAS_ERROR=0 59 | clear 60 | echo -e "$ACTIVITY_HISTORY" 61 | fi 62 | 63 | #check exit code here 64 | sleep $CHECK_INTERVAL 65 | done 66 | 67 | -------------------------------------------------------------------------------- /examples/session.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # Tracks and displays combined crucible stats per gameplay session. 3 | # 4 | # Works on: Windows with Powershell 5 | # If you have permission issues running, see: 6 | # https://stackoverflow.com/a/62403405/10232 7 | # 8 | # Created by Mike Chambers 9 | # https://www.mikechambers.com 10 | # 11 | # Released under an MIT License 12 | # More info at: 13 | # https://github.com/mikechambers/dcli/ 14 | # 15 | # Requires dcliah and dclitime v0.2.0 16 | 17 | 18 | ################ Script configuration ################# 19 | 20 | #run dclim to sync manifest before running this script 21 | 22 | #pull setting from environment variables. you can also 23 | #just enter them here 24 | 25 | #you can get member_id and platform by running dclis 26 | $member_id=$env:MEMBER_ID 27 | $platform=$env:PLATFORM 28 | 29 | #for tracking trials on the weekend mode=trials_of_osiris moment=weekend 30 | $mode="all_pvp" 31 | $moment="now" 32 | 33 | $session_start = (dclitime.exe --moment $moment) 34 | 35 | $check_interval_seconds=30 36 | 37 | ############# program ############# 38 | Clear-Host 39 | Write-Output "Retrieving activity data..." 40 | $last_call_was_error=$false 41 | while ($true) { 42 | 43 | # you could use the Destiny2.exe process detection in ths status_notifications.ps1 44 | # script, and then reset the session_start everytime destiny launches. 45 | # that way, you could keep this script running, and it would always and automatically 46 | # reset your session to when you launch destiny. 47 | 48 | 49 | # assumes dcliah.exe is in your path 50 | $activity = (dcliah.exe --name mesh#3230 ` 51 | --mode $mode --moment custom --custom-time $session_start 2>$null) -join "`n" 52 | #note, to view any errors that might occur, remove 2>$null (this will print 53 | #extra output though, or change to 2>err.txt and it will write to a text file) 54 | 55 | if($LASTEXITCODE) { 56 | if(!$last_call_was_error) { 57 | Write-Host ("Error retrieving activities. Trying again in {0} seconds" -f $check_interval_seconds) -ForegroundColor White -BackgroundColor Red 58 | $last_call_was_error=$true 59 | } 60 | } else { 61 | $last_call_was_error=$false 62 | Clear-Host 63 | Write-Output $activity 64 | } 65 | Start-Sleep -Seconds $check_interval_seconds 66 | } 67 | -------------------------------------------------------------------------------- /examples/status_notification: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Monitors changes in activity status and send notification when it changes 4 | # 5 | # Works on: Mac OS X 6 | # 7 | # Created by Mike Chambers 8 | # https://www.mikechambers.com 9 | # 10 | # Released under an MIT License 11 | # More info at: 12 | # https://github.com/mikechambers/dcli/ 13 | 14 | #https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ 15 | set -u 16 | 17 | #pull variables from environment variables. Otherwise, you can 18 | #just manually set them below 19 | 20 | #you can get your member id and platform from dclim 21 | MEMBER_ID=$MEMBER_ID 22 | PLATFORM=$PLATFORM 23 | 24 | DEFAULT_CHECK_INTERVAL_SECONDS=15 25 | 26 | echo "CTRL-C to end program" 27 | 28 | OLD_ACTIVITY="" 29 | while : 30 | do 31 | CHECK_INTERVAL_SECONDS=$DEFAULT_CHECK_INTERVAL_SECONDS 32 | echo "Checking Status" 33 | ACTIVITY=$(dclia --member-id "$MEMBER_ID" --platform "$PLATFORM") 34 | #note should do some error checking here in case command fails (can capture exit code) 35 | 36 | #we could have the command above output name / value pairs via --output-format tsv 37 | #and then filter based on type of activity (i.e. crucible, strikes, etc...) 38 | if [ "$OLD_ACTIVITY" != "$ACTIVITY" ]; then 39 | echo "Status has changed" 40 | echo "$ACTIVITY" 41 | #note, you could get this running on linux using notify-send command 42 | osascript -e "display notification \"${ACTIVITY}\" with title \"Destiny 2 Activity Changed\"" 43 | OLD_ACTIVITY=$ACTIVITY 44 | 45 | CHECK_INTERVAL_SECONDS=60 46 | fi 47 | 48 | sleep $CHECK_INTERVAL_SECONDS 49 | done 50 | -------------------------------------------------------------------------------- /examples/status_notification.ps1: -------------------------------------------------------------------------------- 1 | 2 | # Monitors changes in activity status and send notification when it changes 3 | # 4 | # Works on: Windows with Powershell 5 | # If you have permission issues running, see: 6 | # https://stackoverflow.com/a/62403405/10232 7 | # 8 | # Created by Mike Chambers 9 | # https://www.mikechambers.com 10 | # 11 | # Released under an MIT License 12 | # More info at: 13 | # https://github.com/mikechambers/dcli/ 14 | # 15 | # Requires dclia v0.1.1 16 | 17 | 18 | ################ Script configuration ################# 19 | $default_check_interval_seconds=15 20 | 21 | #only check if Destiny process is running. Set to $false 22 | #if you want it to check regardless of whether Destiny is running 23 | #on the same machine 24 | $only_check_if_destiny_running = $true 25 | 26 | #whether it should print out status and other output to console 27 | $quiet=$false 28 | 29 | #pull setting from environment variables. you can also 30 | #just enter them here 31 | 32 | #you can get member_id and platform by running dclis 33 | $member_id=$env:MEMBER_ID 34 | $platform=$env:PLATFORM 35 | 36 | #run dclim to sync manifest before running this script 37 | 38 | ############# program ############# 39 | 40 | $old_activity="" 41 | 42 | while ($true) { 43 | 44 | $check_interval_seconds = $default_check_interval_seconds 45 | #check if Destiny is running and whether we should skip check if its not running 46 | $should_check = (Get-Process destiny2 -ErrorAction SilentlyContinue) -or !$only_check_if_destiny_running 47 | 48 | 49 | if ($should_check) { 50 | if (!$quiet) { 51 | Write-Output "Checking Status..." 52 | } 53 | 54 | # assumes dclia is in your path 55 | $activity = (dclia --name mesh#3230) -join "`n" 56 | $skip_notification = (($activity -eq "Not currently in an activity") -or ($activity -eq "Currently sitting in Orbit")) 57 | 58 | #dont send notification the first time we run 59 | if($old_activity -eq "") { 60 | 61 | if (!$quiet) { 62 | Write-Output $activity 63 | Write-Output "Initial status check. Skipping notification." 64 | } 65 | 66 | $skip_notification = $true 67 | $old_activity = $activity 68 | } 69 | 70 | if ( ($old_activity -ne $activity) -and !$skip_notification) 71 | { 72 | [void] [reflection.assembly]::loadwithpartialname("System.Windows.Forms") 73 | [void] [reflection.assembly]::loadwithpartialname("System.Drawing") 74 | $notify = new-object system.windows.forms.notifyicon 75 | $notify.icon = [System.Drawing.SystemIcons]::Information 76 | $notify.visible = $true 77 | $notify.showballoontip(10,"Destiny 2 Activity Changed",$activity,[system.windows.forms.tooltipicon]::None) 78 | 79 | if (!$quiet) { 80 | Write-Output $activity 81 | } 82 | 83 | $old_activity = $activity 84 | $check_interval_seconds = 60 85 | } 86 | } else { 87 | if (!$quiet) { 88 | Write-Output "Destiny 2 is not running. Skipping status check." 89 | } 90 | } 91 | 92 | Start-Sleep -Seconds $check_interval_seconds 93 | } -------------------------------------------------------------------------------- /images/dcliad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/images/dcliad.png -------------------------------------------------------------------------------- /images/dcliad_sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/images/dcliad_sm.png -------------------------------------------------------------------------------- /images/dcliad_x_sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/images/dcliad_x_sm.png -------------------------------------------------------------------------------- /images/dcliah.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/images/dcliah.png -------------------------------------------------------------------------------- /images/dcliah_sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/images/dcliah_sm.png -------------------------------------------------------------------------------- /images/dcliah_x_sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/images/dcliah_x_sm.png -------------------------------------------------------------------------------- /service/README.md: -------------------------------------------------------------------------------- 1 | ## Service 2 | 3 | This folder contains files to run *dclisync* as a system service using [systemctl](https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units) on Linux based systems. 4 | 5 | It consists of two files: 6 | 7 | 8 | | FILE | DESCRIPTION | 9 | | ---- | ----------- | 10 | | dcli_sync_manager.py | A Python 3 based script that manages calls to dclisync 11 | | 12 | | dclisync.service | A systemctl service for dclisync | 13 | 14 | 15 | ### Installation 16 | 17 | First, make sure that you have added the dcli apps to your system path, and have set up 18 | the required environement variables). 19 | 20 | Next, confirm that python 3 is available on your system by running: 21 | 22 | ``` 23 | $which python3 24 | ``` 25 | This should print out the path to the python3 executable (which we will need 26 | below). 27 | 28 | If it is not installed, install it. 29 | 30 | Once python3 is installed, confirm the *dcli_sync_manager.py* script file can 31 | run: 32 | 33 | ``` 34 | $python3 dcli_sync_manager.py 35 | ``` 36 | 37 | Once you configm that it is running you can stop the process / script. 38 | 39 | Before installing the service, you must first edit the *dclisync.service* file. 40 | 41 | ``` 42 | [Unit] 43 | Description=dclisync service 44 | After=multi-user.target 45 | 46 | [Service] 47 | Type=simple 48 | Restart=always 49 | User=mesh 50 | Group=mesh 51 | ExecStart=/usr/bin/bash -lc "/usr/bin/python3 /home/mesh/bin/dcli_sync_manager.py" 52 | 53 | [Install] 54 | WantedBy=multi-user.target 55 | ``` 56 | 57 | Replacing the *User* and *Group* entries with the user and group you want the 58 | service to run under (It does not require admin). It should be the same user 59 | from which you call *dclim*. Then replace the paths to 60 | *python3* and the *dcli_sync_manager.py* files to the correct paths. 61 | 62 | At this point, your service should be ready to install and run. You can find 63 | info [here](https://www.shubhamdipt.com/blog/how-to-create-a-systemd-service-in-linux/) on where to copy the file, and how to start / stop and monitor it. 64 | 65 | Note, when adding new users, you should first stop the service, add and sync 66 | the new user(s) and then restart the service. This ensures multiple processes 67 | are not trying to sync data at the same time, and allows your to monitor the 68 | initial sync for any errors. 69 | 70 | 71 | -------------------------------------------------------------------------------- /service/dcli_sync_manager.py: -------------------------------------------------------------------------------- 1 | import time 2 | from subprocess import run, PIPE 3 | import datetime 4 | 5 | while True: 6 | 7 | p = run( [ 'dclisync', '--sync' ], stdout=PIPE, stderr=PIPE ) 8 | 9 | now = datetime.datetime.now() 10 | if p.returncode == 1: 11 | print( now.strftime("%Y-%m-%d %H:%M:%S"), '[stdout]', p.stdout.decode() ) 12 | print( now.strftime("%Y-%m-%d %H:%M:%S"), '[stderr]', p.stderr.decode() ) 13 | elif p.returncode == 0: 14 | print( now.strftime("%Y-%m-%d %H:%M:%S"), '[stdout]', p.stdout.decode() ) 15 | 16 | time.sleep(30) -------------------------------------------------------------------------------- /service/dclisync.service: -------------------------------------------------------------------------------- 1 | #systemctl for running dcli_sync_manager.py as a service 2 | #run within the users environment 3 | #make sure to replace /home/mesh below with appropriate path 4 | # replace user / group with the appropriate user / group for your system. 5 | # service does not need admin right 6 | 7 | [Unit] 8 | Description=dclisync service 9 | After=multi-user.target 10 | 11 | [Service] 12 | Type=simple 13 | Restart=always 14 | User=mesh 15 | Group=mesh 16 | ExecStart=/usr/bin/bash -lc "/usr/bin/python3 /home/mesh/bin/dcli_sync_manager.py" 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /src/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["dclia", "dclim", "dcliah", "dclitime", "dclisync", "dcliad", "dclistat"] 3 | 4 | #https://github.com/johnthagen/min-sized-rust 5 | [profile.release] 6 | opt-level = 'z' 7 | lto = true 8 | codegen-units = 1 9 | -------------------------------------------------------------------------------- /src/dcli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dcli" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Library for the dcli collection of command line tools for Destiny 2." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | reqwest = { version="0.11.12"} 17 | serde = "1.0.147" 18 | serde_derive = "1.0.147" 19 | serde_json = "1.0.82" 20 | serde_repr = "0.1.9" 21 | zip = "0.5.13" 22 | chrono = "0.4.23" 23 | percent-encoding = "2.1.0" 24 | sqlx = { version ="0.6.2", features=[ "runtime-tokio-rustls", "sqlite" ] } 25 | futures = "0.3.25" 26 | static_assertions = "1.1.0" 27 | crossterm = "0.20.0" 28 | dirs-next = "2.0.0" 29 | log = "0.4.17" 30 | indicatif = "0.17.2" 31 | 32 | tell = { path = "../tell/"} 33 | -------------------------------------------------------------------------------- /src/dcli/README.md: -------------------------------------------------------------------------------- 1 | # dcli 2 | 3 | Library of code for use in the dcli apps. 4 | 5 | In general, the library is specific to dcli, but there are some classes for working with the API and Manifest, as well as some objects for deserializing json data from the API and Manifest. 6 | -------------------------------------------------------------------------------- /src/dcli/actitvity_store_schema.sql: -------------------------------------------------------------------------------- 1 | BEGIN TRANSACTION; 2 | 3 | DROP TABLE IF EXISTS "modes"; 4 | DROP TABLE IF EXISTS "team_result"; 5 | DROP TABLE IF EXISTS "weapon_result"; 6 | DROP TABLE IF EXISTS "medal_result"; 7 | DROP TABLE IF EXISTS "activity_queue"; 8 | DROP TABLE IF EXISTS "character_activity_stats"; 9 | DROP TABLE IF EXISTS "activity"; 10 | DROP TABLE IF EXISTS "character"; 11 | DROP TABLE IF EXISTS "sync"; 12 | DROP TABLE IF EXISTS "member"; 13 | DROP TABLE IF EXISTS "version"; 14 | 15 | DROP INDEX IF EXISTS "modes_activity_index"; 16 | DROP INDEX IF EXISTS "character_activity_stats_char_index"; 17 | DROP INDEX IF EXISTS "activity_period_index"; 18 | 19 | 20 | CREATE TABLE IF NOT EXISTS "main"."version" ( 21 | "version" INTEGER NOT NULL UNIQUE 22 | ); 23 | 24 | INSERT INTO "main"."version"("version") VALUES (10); 25 | 26 | CREATE TABLE IF NOT EXISTS "main"."activity_queue" ( 27 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 28 | "synced" INTEGER NOT NULL DEFAULT 0, 29 | "activity_id" INTEGER NOT NULL, 30 | "character" INTEGER NOT NULL, 31 | UNIQUE("activity_id", "character"), 32 | FOREIGN KEY ("character") 33 | REFERENCES character ("character_id") 34 | ON DELETE CASCADE 35 | ); 36 | 37 | CREATE TABLE IF NOT EXISTS "sync" ( 38 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 39 | "member" INTEGER NOT NULL, 40 | "last_sync" TEXT NOT NULL, 41 | UNIQUE("member"), 42 | FOREIGN KEY ("member") 43 | REFERENCES "member" ("member_id") 44 | ); 45 | 46 | CREATE TABLE IF NOT EXISTS "member" ( 47 | "member_id" INTEGER PRIMARY KEY UNIQUE NOT NULL, 48 | "platform_id" INTEGER NOT NULL, 49 | "display_name" TEXT, 50 | "bungie_display_name" TEXT, 51 | "bungie_display_name_code" TEXT 52 | ); 53 | 54 | CREATE TABLE IF NOT EXISTS "character" ( 55 | "character_id" INTEGER PRIMARY KEY UNIQUE NOT NULL, 56 | "member" INTEGER NOT NULL, 57 | "class" INTEGER NOT NULL, 58 | 59 | FOREIGN KEY ("member") 60 | REFERENCES "member" ("member_id") 61 | ON DELETE CASCADE 62 | ); 63 | 64 | CREATE TABLE IF NOT EXISTS "main"."activity" ( 65 | "activity_id" INTEGER PRIMARY KEY UNIQUE NOT NULL, 66 | "period" TEXT NOT NULL, 67 | "mode" INTEGER NOT NULL, 68 | "platform" INTEGER NOT NULL, 69 | "director_activity_hash" INTEGER NOT NULL, 70 | "reference_id" INTEGER NOT NULL 71 | ); 72 | 73 | CREATE TABLE IF NOT EXISTS "main"."modes" ( 74 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 75 | "mode" INTEGER NOT NULL, 76 | "activity" INTEGER NOT NULL, 77 | UNIQUE("mode", "activity"), 78 | 79 | FOREIGN KEY ("activity") 80 | REFERENCES "activity" ("activity_id") 81 | ON DELETE CASCADE 82 | ); 83 | 84 | CREATE TABLE IF NOT EXISTS "main"."team_result" ( 85 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 86 | "team_id" INTEGER NOT NULL, 87 | "activity" INTEGER NOT NULL, 88 | "score" INTEGER NOT NULL, 89 | "standing" INTEGER NOT NULL, 90 | 91 | UNIQUE("team_id", "activity"), 92 | 93 | FOREIGN KEY ("activity") 94 | REFERENCES "activity" ("activity_id") 95 | ON DELETE CASCADE 96 | ); 97 | 98 | CREATE TABLE IF NOT EXISTS "main"."weapon_result" ( 99 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 100 | 101 | "reference_id" INTEGER NOT NULL, 102 | "kills" INTEGER NOT NULL, 103 | "precision_kills" INTEGER NOT NULL, 104 | "kills_precision_kills_ratio" REAL NOT NULL, 105 | "character_activity_stats" INTEGER NOT NULL, 106 | 107 | UNIQUE("character_activity_stats", "reference_id"), 108 | 109 | FOREIGN KEY ("character_activity_stats") 110 | REFERENCES "character_activity_stats" ("id") 111 | ON DELETE CASCADE 112 | ); 113 | 114 | CREATE TABLE IF NOT EXISTS "main"."medal_result" ( 115 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 116 | "reference_id" INTEGER NOT NULL, 117 | "count" INTEGER NOT NULL, 118 | "character_activity_stats" INTEGER NOT NULL, 119 | 120 | UNIQUE("character_activity_stats", "reference_id"), 121 | 122 | FOREIGN KEY ("character_activity_stats") 123 | REFERENCES "character_activity_stats" ("id") 124 | ON DELETE CASCADE 125 | ); 126 | 127 | CREATE TABLE IF NOT EXISTS "main"."character_activity_stats" ( 128 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, 129 | "character" INTEGER NOT NULL, 130 | "activity" INTEGER NOT NULL, 131 | "assists" INTEGER NOT NULL, 132 | "score" INTEGER NOT NULL, 133 | "kills" INTEGER NOT NULL, 134 | "deaths" INTEGER NOT NULL, 135 | "average_score_per_kill" REAL NOT NULL, 136 | "average_score_per_life" REAL NOT NULL, 137 | "completed" INTEGER NOT NULL, 138 | "opponents_defeated" INTEGER NOT NULL, 139 | "activity_duration_seconds" INTEGER NOT NULL, 140 | "standing" INTEGER NOT NULL, 141 | "team" INTEGER NOT NULL, 142 | "completion_reason" INTEGER NOT NULL, 143 | "start_seconds" INTEGER NOT NULL, 144 | "time_played_seconds" INTEGER NOT NULL, 145 | "player_count" INTEGER NOT NULL, 146 | "team_score" INTEGER NOT NULL, 147 | "precision_kills" INTEGER NOT NULL, 148 | "weapon_kills_ability" INTEGER NOT NULL, 149 | "weapon_kills_grenade" INTEGER NOT NULL, 150 | "weapon_kills_melee" INTEGER NOT NULL, 151 | "weapon_kills_super" INTEGER NOT NULL, 152 | "all_medals_earned" INTEGER NOT NULL, 153 | "light_level" INTEGER NOT NULL, 154 | "emblem_hash" INTEGER NOT NULL, 155 | "fireteam_id" TEXT NOT NULL, 156 | 157 | UNIQUE("activity", "character"), 158 | 159 | FOREIGN KEY ("activity") 160 | REFERENCES "activity" ("activity_id") 161 | ON DELETE CASCADE, 162 | 163 | FOREIGN KEY ("character") 164 | REFERENCES "character" ("character_id") 165 | ON DELETE CASCADE 166 | ); 167 | 168 | CREATE INDEX modes_activity_index ON modes (activity); 169 | CREATE INDEX character_activity_stats_char_index ON character_activity_stats (character); 170 | CREATE INDEX activity_period_index ON activity (period); 171 | 172 | CREATE INDEX idx_character_activity_stats_activity ON character_activity_stats(activity); 173 | 174 | CREATE INDEX idx_character_activity_stats_fireteam_id ON character_activity_stats(fireteam_id); 175 | 176 | COMMIT; -------------------------------------------------------------------------------- /src/dcli/src/apiclient.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use log::{debug, info}; 24 | use tell::{Tell, TellLevel}; 25 | 26 | use reqwest::header::{HeaderMap, HeaderValue, CONNECTION}; 27 | use reqwest::{Client, Url}; 28 | 29 | use crate::error::Error; 30 | use crate::response::drs::{ 31 | check_destiny_response_status, IsDestinyAPIResponse, 32 | }; 33 | 34 | const DESTINY_API_KEY: &str = env!("DESTINY_API_KEY"); 35 | const API_TIMEOUT: u64 = 10; //seconds 36 | 37 | //this makes sure that the env variable isnt set, but empty 38 | static_assertions::const_assert!(!DESTINY_API_KEY.is_empty()); 39 | 40 | pub struct ApiClient { 41 | client: Client, 42 | } 43 | 44 | impl ApiClient { 45 | pub fn new() -> Result { 46 | ApiClient::new_with_key(DESTINY_API_KEY) 47 | } 48 | 49 | pub fn new_with_key(key: &str) -> Result { 50 | let mut headers = HeaderMap::new(); 51 | headers.insert(CONNECTION, HeaderValue::from_static("keep-alive")); 52 | headers.insert( 53 | "Keep-Alive", 54 | HeaderValue::from_static("timeout=10, max=1000"), 55 | ); 56 | 57 | headers.insert("X-API-Key", HeaderValue::from_str(key).unwrap()); 58 | 59 | let client = Client::builder() 60 | .default_headers(headers) 61 | .timeout(std::time::Duration::from_secs(API_TIMEOUT)) 62 | .build()?; 63 | 64 | Ok(ApiClient { client }) 65 | } 66 | 67 | pub async fn call(&self, url: &str) -> Result { 68 | //todo: this could fail if we pass in non-url string 69 | let url = Url::parse(url).unwrap(); 70 | 71 | tell::verbose!("{}", url); 72 | info!("API call : {}", url); 73 | 74 | let response = self 75 | .client 76 | .get(url) 77 | //.header("X-API-Key", DESTINY_API_KEY) 78 | .send() 79 | .await?; //this either returns a reqwest::Response for an Error which is returned 80 | 81 | Ok(response) 82 | } 83 | 84 | pub async fn call_and_parse< 85 | T: serde::de::DeserializeOwned + IsDestinyAPIResponse, 86 | >( 87 | &self, 88 | url: &str, 89 | ) -> Result { 90 | let body = match self.call(url).await { 91 | Ok(e) => { 92 | info!("{:?}", e.headers()); 93 | e.text().await? 94 | } 95 | Err(e) => return Err(e), 96 | }; 97 | 98 | if Tell::is_active(TellLevel::Verbose) { 99 | let len = body.chars().count(); 100 | const MAX: usize = 200; 101 | let limit = std::cmp::min(len, MAX); 102 | 103 | debug!("Response body : {}", body); 104 | let string: String = body.chars().take(limit).skip(0).collect(); 105 | tell::verbose!( 106 | "---------Begin API response : First {} chars---------", 107 | limit 108 | ); 109 | tell::verbose!("{}", string); 110 | info!("First {} chars of response: {}", limit, string); 111 | tell::verbose!("---------End API response---------"); 112 | } 113 | 114 | //we split the parsing from the request so we can capture the body and 115 | //print it out if we need to 116 | let r = serde_json::from_str::(&body)?; 117 | 118 | check_destiny_response_status(r.get_status())?; 119 | 120 | Ok(r) 121 | } 122 | 123 | pub async fn call_post( 124 | &self, 125 | url: &str, 126 | post_data: &str, 127 | ) -> Result { 128 | let url = Url::parse(url).unwrap(); 129 | 130 | info!("Calling API [post] : {}", url); 131 | tell::verbose!("{}", url); 132 | 133 | let response = self 134 | .client 135 | .post(url) 136 | .body(post_data.to_string()) 137 | .send() 138 | .await?; //this either returns a reqwest::Response for an Error which is returned 139 | 140 | Ok(response) 141 | } 142 | 143 | pub async fn call_post_and_parse< 144 | T: serde::de::DeserializeOwned + IsDestinyAPIResponse, 145 | >( 146 | &self, 147 | url: &str, 148 | post_data: &str, 149 | ) -> Result { 150 | let body = match self.call_post(url, post_data).await { 151 | Ok(e) => { 152 | info!("{:?}", e.headers()); 153 | e.text().await? 154 | } 155 | Err(e) => return Err(e), 156 | }; 157 | 158 | if Tell::is_active(TellLevel::Verbose) { 159 | let len = body.chars().count(); 160 | const MAX: usize = 200; 161 | let limit = std::cmp::min(len, MAX); 162 | 163 | tell::verbose!( 164 | "---------Begin API response : First {} chars---------", 165 | limit 166 | ); 167 | tell::verbose!("{}", &body[..limit]); 168 | tell::verbose!("---------End API response---------"); 169 | } 170 | 171 | //we split the parsing from the request so we can capture the body and 172 | //print it out if we need to 173 | let r = serde_json::from_str::(&body)?; 174 | 175 | check_destiny_response_status(r.get_status())?; 176 | 177 | Ok(r) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/dcli/src/apiutils.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | pub const RESOURCE_BASE_URL: &str = "https://www.bungie.net"; 24 | pub const API_BASE_URL: &str = "https://www.bungie.net"; 25 | pub const PGCR_BASE_URL: &str = "https://stats.bungie.net"; 26 | -------------------------------------------------------------------------------- /src/dcli/src/character.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use crate::response::character::CharacterData; 24 | use crate::{enums::character::CharacterClass, response::pgcr::UserInfoCard}; 25 | 26 | pub struct PlayerInfo { 27 | pub characters: Characters, 28 | pub user_info: UserInfoCard, 29 | } 30 | 31 | pub struct Characters { 32 | pub characters: Vec, 33 | } 34 | 35 | impl Characters { 36 | pub fn with_characters(characters: Vec) -> Characters { 37 | let mut out = Characters { characters }; 38 | out.characters 39 | .sort_by(|a, b| b.date_last_played.cmp(&a.date_last_played)); 40 | 41 | out 42 | } 43 | 44 | pub fn get_by_class( 45 | &self, 46 | class_type: CharacterClass, 47 | ) -> Option { 48 | if self.characters.is_empty() { 49 | return None; 50 | } 51 | 52 | for c in &self.characters { 53 | if c.class_type == class_type { 54 | return Some(c.clone()); 55 | } 56 | } 57 | 58 | None 59 | } 60 | 61 | pub fn get_last_active(&self) -> Option { 62 | if self.characters.is_empty() { 63 | return None; 64 | } 65 | 66 | Some(self.characters[0].clone()) 67 | } 68 | 69 | pub fn get_by_class_ref( 70 | &self, 71 | class_type: CharacterClass, 72 | ) -> Option<&CharacterData> { 73 | if self.characters.is_empty() { 74 | return None; 75 | } 76 | 77 | self.characters.iter().find(|&c| c.class_type == class_type) 78 | } 79 | 80 | pub fn get_last_active_ref(&self) -> Option<&CharacterData> { 81 | if self.characters.is_empty() { 82 | return None; 83 | } 84 | 85 | Some(&self.characters[0]) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/dcli/src/cruciblestats.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::ops; 24 | 25 | use crate::utils::{ 26 | calculate_efficiency, calculate_kills_deaths_assists, 27 | calculate_kills_deaths_ratio, 28 | }; 29 | 30 | #[derive(Default, Debug)] 31 | pub struct CrucibleStats { 32 | pub activities_entered: f32, 33 | pub activities_won: f32, 34 | pub activities_lost: f32, 35 | pub assists: f32, 36 | pub kills: f32, 37 | pub average_kill_distance: f32, 38 | pub total_kill_distance: f32, 39 | pub seconds_played: f32, 40 | pub deaths: f32, 41 | pub average_lifespan: f32, 42 | pub total_lifespan: f32, //This is an estimate 43 | pub best_single_game_kills: Option, 44 | pub opponents_defeated: f32, 45 | pub efficiency: f32, 46 | pub kills_deaths_ratio: f32, 47 | pub kills_deaths_assists: f32, 48 | pub suicides: f32, 49 | pub precision_kills: f32, 50 | } 51 | 52 | impl ops::Add for CrucibleStats { 53 | type Output = CrucibleStats; 54 | 55 | fn add(self, _cs: CrucibleStats) -> CrucibleStats { 56 | //note, all of this stuff for single game kills is actually not necessary 57 | //since right now, its only returned when we get all time stats, not daily 58 | //so we dont really every need to aggregate stats. 59 | //but we will keep it here for completeness sake and in case the API is 60 | //ever updated 61 | let best_single_game_kills: Option; 62 | if _cs.best_single_game_kills.is_none() 63 | || self.best_single_game_kills.is_none() 64 | { 65 | if _cs.best_single_game_kills.is_none() { 66 | best_single_game_kills = self.best_single_game_kills; 67 | } else { 68 | best_single_game_kills = _cs.best_single_game_kills; 69 | } 70 | } else { 71 | let a = _cs.best_single_game_kills.unwrap(); 72 | let b = self.best_single_game_kills.unwrap(); 73 | let c = if a > b { a } else { b }; 74 | best_single_game_kills = Some(c); 75 | } 76 | 77 | let kills = self.kills + _cs.kills; 78 | let total_kill_distance = 79 | self.total_kill_distance + _cs.total_kill_distance; 80 | let assists = self.assists + _cs.assists; 81 | let deaths = self.deaths + _cs.deaths; 82 | 83 | let total_lifespan = self.total_lifespan + _cs.total_lifespan; 84 | 85 | //this doesnt completely work, since there are times where a lifespan 86 | //does not end in death (i.e. end of game) 87 | //so when aggregating values, this is an estimate 88 | let average_lifespan = total_lifespan / deaths; 89 | 90 | //todo : add activities_lost 91 | CrucibleStats { 92 | activities_entered: self.activities_entered 93 | + _cs.activities_entered, 94 | activities_won: self.activities_won + _cs.activities_won, 95 | activities_lost: self.activities_lost + _cs.activities_lost, 96 | assists, 97 | kills, 98 | average_kill_distance: total_kill_distance / kills, 99 | total_kill_distance, 100 | seconds_played: self.seconds_played + _cs.seconds_played, 101 | deaths, 102 | average_lifespan, 103 | total_lifespan, 104 | opponents_defeated: self.opponents_defeated 105 | + _cs.opponents_defeated, 106 | efficiency: calculate_efficiency( 107 | kills as u32, 108 | deaths as u32, 109 | assists as u32, 110 | ), 111 | kills_deaths_ratio: calculate_kills_deaths_ratio( 112 | kills as u32, 113 | deaths as u32, 114 | ), 115 | kills_deaths_assists: calculate_kills_deaths_assists( 116 | kills as u32, 117 | deaths as u32, 118 | assists as u32, 119 | ), 120 | suicides: self.suicides + _cs.suicides, 121 | best_single_game_kills, 122 | precision_kills: self.precision_kills + _cs.precision_kills, 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/dcli/src/emblem.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #[derive(PartialEq, Eq, Default, Debug, Clone)] 24 | pub struct Emblem { 25 | pub id: String, 26 | 27 | pub name: String, 28 | 29 | pub icon: String, 30 | pub secondary_icon: String, 31 | pub secondary_special: String, 32 | pub secondary_overlay: String, 33 | //backgroundColor //make a custom color struct 34 | } 35 | -------------------------------------------------------------------------------- /src/dcli/src/enums/character.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use serde_repr::{Deserialize_repr, Serialize_repr}; 24 | 25 | use std::fmt; 26 | use std::str::FromStr; 27 | 28 | #[derive(PartialEq, Eq, Debug, Copy, Clone)] 29 | pub enum CharacterClassSelection { 30 | Titan = 0, 31 | Hunter = 1, 32 | Warlock = 2, 33 | LastActive = 3, 34 | All = 4, 35 | } 36 | 37 | impl CharacterClassSelection { 38 | pub fn as_id(&self) -> u32 { 39 | *self as u32 40 | } 41 | } 42 | 43 | impl FromStr for CharacterClassSelection { 44 | type Err = &'static str; 45 | 46 | fn from_str(s: &str) -> Result { 47 | //wrap in String so we can convert to lower case 48 | let s = String::from(s).to_lowercase(); 49 | 50 | //get a slice to get a &str for the match 51 | match &s[..] { 52 | "titan" => Ok(CharacterClassSelection::Titan), 53 | "hunter" => Ok(CharacterClassSelection::Hunter), 54 | "warlock" => Ok(CharacterClassSelection::Warlock), 55 | "last_active" => Ok(CharacterClassSelection::LastActive), 56 | "all" => Ok(CharacterClassSelection::All), 57 | _ => Err("Unknown CharacterClassSelection type"), 58 | } 59 | } 60 | } 61 | 62 | /****************CharacterGender *******************/ 63 | #[derive( 64 | PartialEq, Eq, Clone, Copy, Serialize_repr, Deserialize_repr, Debug, 65 | )] 66 | #[repr(u32)] 67 | pub enum CharacterGender { 68 | Masculine = 0, 69 | Feminine = 1, 70 | } 71 | 72 | impl CharacterGender { 73 | pub fn as_id(&self) -> u32 { 74 | *self as u32 75 | } 76 | 77 | pub fn from_id(id: u64) -> CharacterGender { 78 | match id { 79 | 0 => CharacterGender::Masculine, 80 | 1 => CharacterGender::Feminine, 81 | _ => panic!("Unknkown Character Gender Id : {}", id), 82 | } 83 | } 84 | } 85 | 86 | impl fmt::Display for CharacterGender { 87 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 88 | let out = match self { 89 | CharacterGender::Masculine => "Masculine", 90 | CharacterGender::Feminine => "Feminine", 91 | }; 92 | 93 | check_width(out, f) 94 | } 95 | } 96 | 97 | /****************CharacterClass *******************/ 98 | #[derive( 99 | PartialEq, Eq, Hash, Clone, Copy, Serialize_repr, Deserialize_repr, Debug, 100 | )] 101 | #[repr(u32)] 102 | pub enum CharacterClass { 103 | Titan = 0, 104 | Hunter = 1, 105 | Warlock = 2, 106 | Unknown = 255, 107 | } 108 | 109 | impl CharacterClass { 110 | pub fn as_id(&self) -> u32 { 111 | *self as u32 112 | } 113 | 114 | pub fn from_id(id: u32) -> CharacterClass { 115 | match id { 116 | 0 => CharacterClass::Titan, 117 | 1 => CharacterClass::Hunter, 118 | 2 => CharacterClass::Warlock, 119 | _ => CharacterClass::Unknown, 120 | } 121 | } 122 | pub fn from_hash(id: u32) -> CharacterClass { 123 | match id { 124 | 3655393761 => CharacterClass::Titan, 125 | 671679327 => CharacterClass::Hunter, 126 | 2271682572 => CharacterClass::Warlock, 127 | _ => CharacterClass::Unknown, 128 | } 129 | } 130 | } 131 | fn check_width(s: &str, f: &mut fmt::Formatter) -> fmt::Result { 132 | if let Some(width) = f.width() { 133 | write!(f, "{:width$}", s.to_string(), width = width) 134 | } else { 135 | write!(f, "{}", s) 136 | } 137 | } 138 | impl fmt::Display for CharacterClass { 139 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 140 | let out = match self { 141 | CharacterClass::Titan => "Titan", 142 | CharacterClass::Hunter => "Hunter", 143 | CharacterClass::Warlock => "Warlock", 144 | CharacterClass::Unknown => "Unknown", 145 | }; 146 | 147 | check_width(out, f) 148 | } 149 | } 150 | 151 | /*************************** CharacterRace *************************/ 152 | 153 | #[derive( 154 | PartialEq, Eq, Clone, Copy, Serialize_repr, Deserialize_repr, Debug, 155 | )] 156 | #[repr(u32)] 157 | pub enum CharacterRace { 158 | Human = 0, 159 | Awoken = 1, 160 | Exo = 2, 161 | } 162 | 163 | impl CharacterRace { 164 | pub fn as_id(&self) -> u32 { 165 | *self as u32 166 | } 167 | 168 | pub fn from_id(id: u64) -> CharacterRace { 169 | match id { 170 | 0 => CharacterRace::Human, 171 | 1 => CharacterRace::Awoken, 172 | 2 => CharacterRace::Exo, 173 | _ => panic!("Unknkown Character Race Id : {}", id), 174 | } 175 | } 176 | } 177 | 178 | impl fmt::Display for CharacterRace { 179 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 180 | let out = match self { 181 | CharacterRace::Human => "Human", 182 | CharacterRace::Awoken => "Awoken", 183 | CharacterRace::Exo => "Exo", 184 | }; 185 | 186 | check_width(out, f) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/dcli/src/enums/completionreason.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::fmt; 24 | 25 | ///Destiny 2 Platforms 26 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 27 | #[repr(i32)] 28 | pub enum CompletionReason { 29 | ObjectiveComplete = 0, 30 | Failed = 2, 31 | TimerFinished = 1, 32 | NoOpponents = 3, 33 | Mercy = 4, 34 | Unknown = 255, 35 | } 36 | 37 | impl CompletionReason { 38 | pub fn as_id(&self) -> u32 { 39 | *self as u32 40 | } 41 | 42 | pub fn from_id(id: u32) -> CompletionReason { 43 | match id { 44 | 0 => CompletionReason::ObjectiveComplete, 45 | 1 => CompletionReason::TimerFinished, 46 | 2 => CompletionReason::Failed, 47 | 3 => CompletionReason::NoOpponents, 48 | 4 => CompletionReason::Mercy, 49 | 255 => CompletionReason::Unknown, 50 | _ => CompletionReason::Unknown, 51 | } 52 | } 53 | } 54 | 55 | impl fmt::Display for CompletionReason { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | let out = match self { 58 | CompletionReason::ObjectiveComplete => "Objective Complete", 59 | CompletionReason::TimerFinished => "Timer Finished", 60 | CompletionReason::Failed => "Failed", 61 | CompletionReason::NoOpponents => "No Opponents", 62 | CompletionReason::Mercy => "Mercy", 63 | CompletionReason::Unknown => "Unknown", 64 | }; 65 | 66 | write!(f, "{}", out) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/dcli/src/enums/itemtype.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use serde_repr::{Deserialize_repr, Serialize_repr}; 24 | 25 | #[derive(Eq, PartialEq, Debug, Clone, Deserialize_repr, Serialize_repr)] 26 | #[repr(i32)] 27 | pub enum ItemType { 28 | Unknown = -1, 29 | None = 0, 30 | Currency = 1, 31 | Armor = 2, 32 | Weapon = 3, 33 | Message = 7, 34 | Engram = 8, 35 | Consumable = 9, 36 | ExchangeMaterial = 10, 37 | MissionReward = 11, 38 | QuestStep = 12, 39 | QuestStepComplete = 13, 40 | Emblem = 14, 41 | Quest = 15, 42 | Subclass = 16, 43 | ClanBanner = 17, 44 | Aura = 18, 45 | Mod = 19, 46 | Dummy = 20, 47 | Ship = 21, 48 | Vehicle = 22, 49 | Emote = 23, 50 | Ghost = 24, 51 | Package = 25, 52 | Bounty = 26, 53 | Wrapper = 27, 54 | SeasonalArtifact = 28, 55 | Finisher = 29, 56 | Pattern = 30, 57 | } 58 | 59 | #[derive(Eq, PartialEq, Debug, Clone, Deserialize_repr, Serialize_repr)] 60 | #[repr(i32)] 61 | pub enum ItemSubType { 62 | Unknown = -1, 63 | None = 0, 64 | Crucible = 1, 65 | Vanguard = 2, 66 | Exotic = 5, 67 | AutoRifle = 6, 68 | Shotgun = 7, 69 | Machinegun = 8, 70 | HandCannon = 9, 71 | RocketLauncher = 10, 72 | FusionRifle = 11, 73 | SniperRifle = 12, 74 | PulseRifle = 13, 75 | ScoutRifle = 14, 76 | Crm = 16, 77 | Sidearm = 17, 78 | Sword = 18, 79 | Mask = 19, 80 | Shader = 20, 81 | Ornament = 21, 82 | FusionRifleLine = 22, 83 | GrenadeLauncher = 23, 84 | SubmachineGun = 24, 85 | TraceRifle = 25, 86 | HelmetArmor = 26, 87 | GauntletsArmor = 27, 88 | ChestArmor = 28, 89 | LegArmor = 29, 90 | ClassArmor = 30, 91 | Bow = 31, 92 | DummyRepeatableBounty = 32, 93 | Glaive = 33, 94 | } 95 | 96 | impl std::fmt::Display for ItemSubType { 97 | // This trait requires `fmt` with this exact signature. 98 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 99 | let out = match self { 100 | ItemSubType::Unknown => "Unknown".to_string(), 101 | ItemSubType::AutoRifle => "Auto Rifle".to_string(), 102 | ItemSubType::Machinegun => "Machine Gun".to_string(), 103 | ItemSubType::HandCannon => "Hand Cannon".to_string(), 104 | ItemSubType::RocketLauncher => "Rocket Launcher".to_string(), 105 | ItemSubType::FusionRifle => "Fusion Rifle".to_string(), 106 | ItemSubType::SniperRifle => "Sniper Rifle".to_string(), 107 | ItemSubType::PulseRifle => "Pulse Rifle".to_string(), 108 | ItemSubType::ScoutRifle => "Scout Rifle".to_string(), 109 | ItemSubType::FusionRifleLine => "Linear Fusion Rifle".to_string(), 110 | ItemSubType::GrenadeLauncher => "Grenade Launcher".to_string(), 111 | ItemSubType::SubmachineGun => "Submachine Gun".to_string(), 112 | ItemSubType::TraceRifle => "Trace Rifle".to_string(), 113 | ItemSubType::Glaive => "Glaive".to_string(), 114 | ItemSubType::HelmetArmor => "Helmet".to_string(), 115 | ItemSubType::GauntletsArmor => "Gauntlets".to_string(), 116 | ItemSubType::ChestArmor => "Chest".to_string(), 117 | ItemSubType::LegArmor => "Legs".to_string(), 118 | ItemSubType::ClassArmor => "Class Armor".to_string(), 119 | _ => format!("{:?}", self), 120 | }; 121 | 122 | write!(f, "{}", out) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/dcli/src/enums/medaltier.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use serde_repr::{Deserialize_repr, Serialize_repr}; 24 | 25 | #[derive(Eq, PartialEq, Debug, Clone, Deserialize_repr, Serialize_repr)] 26 | #[repr(u32)] 27 | pub enum MedalTier { 28 | Tier1 = 802673300, 29 | Tier2 = 802673303, 30 | Tier3 = 802673302, 31 | Tier4 = 802673297, 32 | Tier5 = 802673296, 33 | Tier6 = 802673299, 34 | Tier7 = 802673298, 35 | Unknown = 0, 36 | } 37 | impl MedalTier { 38 | pub fn get_order(&self) -> u32 { 39 | match self { 40 | MedalTier::Tier1 => 700, 41 | MedalTier::Tier2 => 600, 42 | MedalTier::Tier3 => 500, 43 | MedalTier::Tier4 => 400, 44 | MedalTier::Tier5 => 300, 45 | MedalTier::Tier6 => 200, 46 | MedalTier::Tier7 => 100, 47 | MedalTier::Unknown => 0, 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/dcli/src/enums/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | pub mod character; 24 | pub mod completionreason; 25 | pub mod itemtype; 26 | pub mod medaltier; 27 | pub mod mode; 28 | pub mod moment; 29 | pub mod platform; 30 | pub mod standing; 31 | pub mod stat; 32 | pub mod weaponsort; 33 | -------------------------------------------------------------------------------- /src/dcli/src/enums/platform.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::fmt; 24 | use std::str::FromStr; 25 | 26 | use serde_repr::{Deserialize_repr, Serialize_repr}; 27 | 28 | ///Destiny 2 Platforms 29 | #[derive( 30 | PartialEq, Clone, Copy, Debug, Hash, Eq, Deserialize_repr, Serialize_repr, 31 | )] 32 | #[repr(i32)] 33 | pub enum Platform { 34 | //API will return Unknown a lot 35 | Unknown = 0, 36 | Xbox = 1, 37 | Playstation = 2, 38 | Steam = 3, 39 | Blizzard = 4, 40 | Stadia = 5, 41 | Epic = 6, 42 | } 43 | 44 | /* 45 | https://bungie-net.github.io/multi/schema_BungieMembershipType.html#schema_BungieMembershipType 46 | None: 0 47 | TigerXbox: 1 48 | TigerPsn: 2 49 | TigerSteam: 3 50 | TigerBlizzard: 4 51 | TigerStadia: 5 52 | Epic : 6 53 | TigerDemon: 10 54 | BungieNext: 254 55 | */ 56 | impl Platform { 57 | pub fn as_id(&self) -> u32 { 58 | *self as u32 59 | } 60 | 61 | pub fn from_id(id: u32) -> Platform { 62 | match id { 63 | 1 => Platform::Xbox, 64 | 2 => Platform::Playstation, 65 | 3 => Platform::Steam, 66 | 4 => Platform::Blizzard, 67 | 5 => Platform::Stadia, 68 | 6 => Platform::Epic, 69 | _ => Platform::Unknown, 70 | } 71 | } 72 | } 73 | 74 | impl FromStr for Platform { 75 | type Err = &'static str; 76 | 77 | fn from_str(s: &str) -> Result { 78 | //wrap in String so we can convert to lower case 79 | let s = String::from(s).to_lowercase(); 80 | 81 | //get a slice to get a &str for the match 82 | match &s[..] { 83 | "xbox" => Ok(Platform::Xbox), 84 | "playstation" => Ok(Platform::Playstation), 85 | "steam" => Ok(Platform::Steam), 86 | "stadia" => Ok(Platform::Stadia), 87 | "blizzard" => Ok(Platform::Blizzard), 88 | "epic" => Ok(Platform::Epic), 89 | _ => Err("Unknown platform type"), 90 | } 91 | } 92 | } 93 | 94 | impl fmt::Display for Platform { 95 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 96 | let out = match self { 97 | Platform::Xbox => "Xbox", 98 | Platform::Playstation => "Playstation", 99 | Platform::Steam => "Steam", 100 | Platform::Stadia => "Stadia", 101 | Platform::Blizzard => "Blizzard", 102 | Platform::Epic => "Epic", 103 | Platform::Unknown => "Unknown", 104 | }; 105 | 106 | write!(f, "{}", out) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/dcli/src/enums/standing.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::fmt; 24 | 25 | use crate::enums::mode::Mode; 26 | 27 | pub const STANDING_UNKNOWN_MAGIC_NUMBER: u32 = 2325; 28 | 29 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 30 | #[repr(u32)] 31 | pub enum Standing { 32 | Victory = 0, 33 | Defeat = 1, 34 | Unknown = STANDING_UNKNOWN_MAGIC_NUMBER, 35 | } 36 | 37 | impl Default for Standing { 38 | fn default() -> Self { 39 | Standing::Unknown 40 | } 41 | } 42 | 43 | impl Standing { 44 | //todo: any value except 1 is probably defeat 45 | pub fn from_value(value: u32) -> Standing { 46 | if value == 0 { 47 | Standing::Victory 48 | } else { 49 | Standing::Defeat 50 | } 51 | } 52 | 53 | pub fn from_mode(value: u32, mode: &Mode) -> Standing { 54 | if value == 0 { 55 | return Standing::Victory; 56 | } else if value == STANDING_UNKNOWN_MAGIC_NUMBER { 57 | return Standing::Unknown; 58 | } 59 | 60 | if value > 0 { 61 | if mode == &Mode::Rumble && value > 2 { 62 | Standing::Defeat 63 | } else { 64 | Standing::Victory 65 | } 66 | } else { 67 | Standing::Victory 68 | } 69 | } 70 | } 71 | 72 | impl fmt::Display for Standing { 73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 | let out = match self { 75 | Standing::Victory => "Victory", 76 | Standing::Defeat => "Defeat", 77 | Standing::Unknown => "Unknown", 78 | }; 79 | 80 | write!(f, "{}", out) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/dcli/src/enums/stat.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::fmt; 24 | use std::str::FromStr; 25 | 26 | #[derive(Eq, PartialEq, Debug, Copy, Clone)] 27 | pub enum Stat { 28 | KD, 29 | Efficiency, 30 | KDA, 31 | Kills, 32 | OpponentsDefeated, 33 | Deaths, 34 | Assists, 35 | KillsAvg, 36 | OpponentsDefeatedAvg, 37 | DeathsAvg, 38 | AssistsAvg, 39 | 40 | KDMax, 41 | EfficiencyMax, 42 | KDAMax, 43 | KillsMax, 44 | OpponentsDefeatedMax, 45 | DeathsMax, 46 | AssistsMax, 47 | 48 | Games, 49 | Wins, 50 | Losses, 51 | Mercies, 52 | } 53 | 54 | impl FromStr for Stat { 55 | type Err = &'static str; 56 | 57 | fn from_str(s: &str) -> Result { 58 | //wrap in String so we can convert to lower case 59 | let s = String::from(s).to_lowercase(); 60 | 61 | //get a slice to get a &str for the match 62 | match &s[..] { 63 | "kd" => Ok(Stat::KD), 64 | "efficiency" => Ok(Stat::Efficiency), 65 | "kda" => Ok(Stat::KDA), 66 | "kills" => Ok(Stat::Kills), 67 | "opponents_defeated" => Ok(Stat::OpponentsDefeated), 68 | "deaths" => Ok(Stat::Deaths), 69 | "assists" => Ok(Stat::Assists), 70 | "kills_avg" => Ok(Stat::KillsAvg), 71 | "opponents_defeated_avg" => Ok(Stat::OpponentsDefeatedAvg), 72 | "deaths_avg" => Ok(Stat::DeathsAvg), 73 | "assists_avg" => Ok(Stat::AssistsAvg), 74 | 75 | "kd_max" => Ok(Stat::KDMax), 76 | "efficiency_max" => Ok(Stat::EfficiencyMax), 77 | "kda_max" => Ok(Stat::KDAMax), 78 | "kills_max" => Ok(Stat::KillsMax), 79 | "deaths_max" => Ok(Stat::DeathsMax), 80 | "assists_max" => Ok(Stat::AssistsMax), 81 | "opponents_defeated_max" => Ok(Stat::OpponentsDefeatedMax), 82 | 83 | "games" => Ok(Stat::Games), 84 | "wins" => Ok(Stat::Wins), 85 | "losses" => Ok(Stat::Losses), 86 | "mercies" => Ok(Stat::Mercies), 87 | 88 | _ => Err("Unknown Stat type"), 89 | } 90 | } 91 | } 92 | 93 | impl fmt::Display for Stat { 94 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 95 | let out = match self { 96 | Stat::KD => "Kills Deaths Ratio", 97 | Stat::Efficiency => "Efficiency", 98 | Stat::KDA => "Kills Deaths Assists Ratio", 99 | Stat::Kills => "Kills", 100 | Stat::OpponentsDefeated => "Opponents defeated", 101 | Stat::Deaths => "Deaths", 102 | Stat::Assists => "Assists", 103 | Stat::KillsAvg => "Kills per game", 104 | Stat::DeathsAvg => "Deaths per game", 105 | Stat::OpponentsDefeatedAvg => "Opponents defeated per game", 106 | Stat::AssistsAvg => "Assists per game", 107 | 108 | Stat::KDMax => "Highest game kills deaths ration", 109 | Stat::EfficiencyMax => "Highest game efficiency", 110 | Stat::KDAMax => "Highest game kills deaths assists ratio", 111 | Stat::KillsMax => "Highest kills in a game", 112 | Stat::DeathsMax => "Highest deaths in a game", 113 | Stat::AssistsMax => "Highest assists in a game", 114 | Stat::OpponentsDefeatedMax => { 115 | "Highest opponents deafeated in a game" 116 | } 117 | 118 | Stat::Games => "games", 119 | Stat::Wins => "wins", 120 | Stat::Losses => "losses", 121 | Stat::Mercies => "games ending in mercy", 122 | }; 123 | 124 | write!(f, "{}", out) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/dcli/src/enums/weaponsort.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | //use std::fmt; 24 | use std::str::FromStr; 25 | 26 | #[derive(Eq, PartialEq, Debug)] 27 | pub enum WeaponSort { 28 | Name, 29 | Kills, 30 | Games, 31 | KillsPerGameKills, 32 | PrecisionTotal, 33 | PrecisionPercent, 34 | WinPercent, 35 | Type, 36 | } 37 | 38 | impl FromStr for WeaponSort { 39 | type Err = &'static str; 40 | 41 | fn from_str(s: &str) -> Result { 42 | //wrap in String so we can convert to lower case 43 | let s = String::from(s).to_lowercase(); 44 | 45 | //get a slice to get a &str for the match 46 | match &s[..] { 47 | "name" => Ok(WeaponSort::Name), 48 | "kills" => Ok(WeaponSort::Kills), 49 | "games" => Ok(WeaponSort::Games), 50 | "kills_per_game_kills" => Ok(WeaponSort::KillsPerGameKills), 51 | "precision_total" => Ok(WeaponSort::PrecisionTotal), 52 | "precision_percent" => Ok(WeaponSort::PrecisionPercent), 53 | "wins_percent" => Ok(WeaponSort::WinPercent), 54 | "type" => Ok(WeaponSort::Type), 55 | 56 | _ => Err("Unknown WeaponSort type"), 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/dcli/src/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | //For error handling approach, we are going to start with one error for all 24 | //APIs and individual apps. Given that there is only a general range of what the apps 25 | //do, mostly loading and parsing api data, then we should be able to cover 26 | //error cases without super ballooning the number of error types. 27 | //If it turns out this becomes unwieldy, then we will break it out, into API 28 | //and app specific errors 29 | 30 | use std::fmt::{Display, Formatter, Result}; 31 | 32 | use crate::response::activities::MAX_ACTIVITIES_REQUEST_COUNT; 33 | 34 | #[derive(Eq, PartialEq, Debug)] 35 | pub enum Error { 36 | ApiRequest { description: String }, 37 | ApiStatus { description: String }, 38 | ApiResponseMissing, 39 | 40 | //when parameters are malformed in wrong format (i.e. expecting id, getting a name) 41 | ParameterParseFailure, 42 | //when id & platform are not correct combination 43 | InvalidParameters, 44 | //Api key not set correctly 45 | ApiKeyMissingFromRequest, 46 | ApiNotAvailableException, 47 | RequestTimedOut, 48 | Request, 49 | PrivacyException, 50 | Database { description: String }, 51 | ApiParse { description: String }, 52 | IoError { description: String }, 53 | IoErrorDirIsFile { description: String }, 54 | IoFileDoesNotExist { description: String }, 55 | ZipError { description: String }, 56 | Unknown { description: String }, 57 | ManifestNotSet, 58 | ManifestItemNotFound { description: String }, 59 | MaxActivitiesRequestCountExceeded, 60 | CharacterDataNotFound, 61 | SystemDirectoryNotFound, 62 | ChronoParse { description: String }, 63 | UnknownEnumValue, 64 | NoCharacters, 65 | CharacterDoesNotExist, 66 | ActivityNotFound, 67 | DateTimePeriodOrder, 68 | BungieNameNotFound, 69 | NoProfilesFound, 70 | InvalidArgument { description: String }, 71 | } 72 | 73 | impl Display for Error { 74 | fn fmt(&self, f: &mut Formatter) -> Result { 75 | match self { 76 | Error::ApiRequest { description } => { 77 | write!(f, "Error calling Destiny 2 API. {}", description) 78 | }, 79 | Error::InvalidArgument { description } => { 80 | write!(f, "Invalid Argument. {}", description) 81 | }, 82 | Error::ApiStatus { description } => { 83 | write!(f, "Destiny 2 API call returned an error. {}", description) 84 | }, 85 | Error::ApiParse { description } => write!( 86 | f, 87 | "Error parsing results from Destiny 2 API call. {}", 88 | description 89 | ), 90 | Error::IoError { description } => { 91 | write!(f, "Error working with file system. {}", description) 92 | }, 93 | Error::ZipError { description } => { 94 | write!(f, "Error decompressing manifest. {}", description) 95 | }, 96 | Error::IoErrorDirIsFile { description } => { 97 | write!(f, "Expected directory but found file. {}", description) 98 | }, 99 | Error::Unknown { description } => { 100 | write!(f, "An unknown error occured. {}", description) 101 | }, 102 | Error::ParameterParseFailure => write!(f, "Could not parse Parameters. Make sure your inputs were correct and try again. (code 7)"), 103 | Error::InvalidParameters => write!(f, "Invalid input parameters. (code 18)"), 104 | Error::ManifestNotSet => write!(f, "Manifest was not set in Manifest Interface."), 105 | Error::ApiKeyMissingFromRequest => write!( 106 | f, 107 | "Missing API Key. Set DESTINY_API_KEY environment variable before compiling." 108 | ), 109 | Error::ApiNotAvailableException => { 110 | write!(f, "The Destiny API is currently not available. Please try again later.") 111 | }, 112 | Error::PrivacyException => write!( 113 | f, 114 | "Privacy settings for Bungie account are too restrictive." 115 | ), 116 | Error::IoFileDoesNotExist { description } => { 117 | write!(f, "Expected File does not exist: {}", description) 118 | }, 119 | Error::Database { description } => { 120 | write!(f, "Error working with SQLite database : {}", description) 121 | }, 122 | Error::ManifestItemNotFound { description } => { 123 | write!(f, "Manifest Item not found : {}", description) 124 | }, 125 | Error::ApiResponseMissing => write!( 126 | f, 127 | "Received response from API but no response property was present." 128 | ), 129 | Error::RequestTimedOut => write!( 130 | f, 131 | "The API request took too long. Check your network connection and \ 132 | try again. (The API servers may be slow right now)." 133 | ), 134 | Error::Request => write!( 135 | f, 136 | "There was an error during the API request. This often means \ 137 | that we could not reach the Destiny servers. Check the network \ 138 | connection and try again (The API servers might not be available.)." 139 | ), 140 | 141 | Error::MaxActivitiesRequestCountExceeded => write!( 142 | f, 143 | "The maximum number of activities ({}) requested was exceeded.", 144 | MAX_ACTIVITIES_REQUEST_COUNT 145 | ), 146 | Error::CharacterDataNotFound => write!( 147 | f, 148 | "Could not find entry in activity data for specified character." 149 | ), 150 | Error::SystemDirectoryNotFound => { 151 | write!(f, "Could not locate system directory.") 152 | }, 153 | Error::ChronoParse { description } => { 154 | write!(f, "Error parsing String to date / time : {}", description) 155 | }, 156 | Error::UnknownEnumValue => { 157 | write!(f, "Could not convert value to enum.") 158 | }, 159 | Error::NoCharacters => { 160 | write!(f, "There are no characters for the member.") 161 | }, 162 | Error::CharacterDoesNotExist => { 163 | write!(f, "Character class does not exist for member.") 164 | }, 165 | Error::ActivityNotFound => { 166 | write!(f, "Could not find activity in data store.") 167 | }, 168 | Error::DateTimePeriodOrder => { 169 | write!(f, "Start date must be before end date.") 170 | }, 171 | Error::BungieNameNotFound => { 172 | write!(f, "Bungie name not found.") 173 | }, 174 | Error::NoProfilesFound => { 175 | write!(f, "No player profiles found.") 176 | }, 177 | } 178 | } 179 | } 180 | 181 | impl From for Error { 182 | fn from(err: serde_json::Error) -> Error { 183 | Error::ApiParse { 184 | description: format!("serde_json::Error : {:#?}", err), 185 | } //TODO:: impliment this for all error types 186 | } 187 | } 188 | 189 | impl From for Error { 190 | fn from(err: reqwest::Error) -> Error { 191 | /* 192 | //todo: need to figure out how to downcast to hyber error 193 | //so we can get more details on the error (i.e. network failure) 194 | //https://stackoverflow.com/a/61100595/10232 195 | let hyper_error: Option<&hyper::Error> = reqwest_error 196 | .source() 197 | .unwrap() 198 | .downcast_ref(); 199 | */ 200 | 201 | if err.is_timeout() { 202 | Error::RequestTimedOut 203 | } else if err.is_request() { 204 | Error::Request 205 | } else { 206 | Error::ApiRequest { 207 | description: format!("reqwest::Error : {:#?}", err), 208 | } 209 | } 210 | } 211 | } 212 | 213 | impl From for Error { 214 | fn from(err: std::io::Error) -> Error { 215 | Error::IoError { 216 | description: format!("std::io::Error : {:#?}", err), 217 | } 218 | } 219 | } 220 | 221 | impl From for Error { 222 | fn from(err: zip::result::ZipError) -> Error { 223 | Error::ZipError { 224 | description: format!("zip::result::ZipError : {:#?}", err), 225 | } 226 | } 227 | } 228 | 229 | impl From for Error { 230 | fn from(err: sqlx::Error) -> Error { 231 | Error::Database { 232 | description: format!("sqlx::Error : {:#?}", err), 233 | } 234 | } 235 | } 236 | 237 | impl From for Error { 238 | fn from(err: chrono::format::ParseError) -> Error { 239 | Error::ChronoParse { 240 | description: format!("chrono::format::ParseError : {:#?}", err), 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/dcli/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | pub mod activitystoreinterface; 24 | pub mod apiclient; 25 | pub mod apiinterface; 26 | pub mod apiutils; 27 | pub mod character; 28 | pub mod crucible; 29 | pub mod cruciblestats; 30 | pub mod emblem; 31 | pub mod enums; 32 | pub mod error; 33 | pub mod manifest; 34 | pub mod manifestinterface; 35 | pub mod output; 36 | pub mod playeractivitiessummary; 37 | pub mod response; 38 | pub mod statscontainer; 39 | pub mod utils; 40 | -------------------------------------------------------------------------------- /src/dcli/src/manifest/definitions.rs: -------------------------------------------------------------------------------- 1 | use serde_derive::{Deserialize, Serialize}; 2 | 3 | use crate::enums::itemtype::{ItemSubType, ItemType}; 4 | use crate::enums::medaltier::MedalTier; 5 | use crate::response::utils::prepend_base_url_option; 6 | 7 | #[derive(Serialize, Deserialize, Debug, Clone)] 8 | pub struct DisplayPropertiesData { 9 | pub description: Option, 10 | pub name: String, 11 | 12 | //https://stackoverflow.com/a/44303505/10232 13 | #[serde(default)] 14 | #[serde(rename = "icon", deserialize_with = "prepend_base_url_option")] 15 | pub icon_path: Option, 16 | 17 | #[serde(rename = "hasIcon")] 18 | pub has_icon: bool, 19 | } 20 | 21 | #[derive(Serialize, Deserialize, Debug, Clone)] 22 | pub struct InventoryItemDefinitionData { 23 | #[serde(rename = "hash")] 24 | pub id: u32, 25 | 26 | #[serde(rename = "displayProperties")] 27 | pub display_properties: DisplayPropertiesData, 28 | 29 | #[serde(rename = "itemTypeDisplayName")] 30 | pub item_type_display_name: Option, 31 | 32 | #[serde(rename = "itemTypeAndTierDisplayName")] 33 | pub item_type_and_tier_display_name: Option, 34 | 35 | #[serde(rename = "itemType")] 36 | pub item_type: ItemType, 37 | 38 | #[serde(rename = "itemSubType")] 39 | pub item_sub_type: ItemSubType, 40 | } 41 | 42 | #[derive(Serialize, Deserialize, Debug, Clone)] 43 | pub struct ActivityDefinitionData { 44 | #[serde(rename = "hash")] 45 | pub id: u32, 46 | 47 | #[serde(rename = "displayProperties")] 48 | pub display_properties: DisplayPropertiesData, 49 | 50 | #[serde(default)] 51 | #[serde( 52 | rename = "pgcrImage", 53 | deserialize_with = "prepend_base_url_option" 54 | )] 55 | pub pgcr_image: Option, 56 | 57 | #[serde(rename = "destinationHash")] 58 | pub destination_hash: u32, 59 | 60 | #[serde(rename = "placeHash")] 61 | pub place_hash: u32, 62 | 63 | #[serde(rename = "activityTypeHash")] 64 | pub activity_type_hash: u32, 65 | } 66 | 67 | #[derive(Serialize, Deserialize, Debug)] 68 | pub struct DestinationDefinitionData { 69 | #[serde(rename = "hash")] 70 | pub id: u32, 71 | 72 | #[serde(rename = "displayProperties")] 73 | pub display_properties: DisplayPropertiesData, 74 | 75 | #[serde(rename = "placeHash")] 76 | pub place_hash: u32, 77 | } 78 | 79 | #[derive(Serialize, Deserialize, Debug, Clone)] 80 | pub struct HistoricalStatsDefinition { 81 | #[serde(rename = "statId")] 82 | pub id: String, 83 | 84 | #[serde(rename = "statName")] 85 | pub name: String, 86 | 87 | #[serde(default, rename = "statDescription")] 88 | pub description: String, 89 | 90 | #[serde(rename = "iconImage")] 91 | pub icon_image_path: Option, 92 | 93 | pub weight: i32, 94 | 95 | #[serde(rename = "medalTierHash")] 96 | pub medal_tier: Option, 97 | } 98 | 99 | #[derive(Serialize, Deserialize, Debug)] 100 | pub struct PlaceDefinitionData { 101 | #[serde(rename = "hash")] 102 | pub id: u32, 103 | 104 | #[serde(rename = "displayProperties")] 105 | pub display_properties: DisplayPropertiesData, 106 | } 107 | 108 | #[derive(Serialize, Deserialize, Debug)] 109 | pub struct ActivityTypeDefinitionData { 110 | #[serde(rename = "hash")] 111 | pub id: u32, 112 | 113 | #[serde(rename = "displayProperties")] 114 | pub display_properties: DisplayPropertiesData, 115 | } 116 | -------------------------------------------------------------------------------- /src/dcli/src/manifest/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | pub mod definitions; 24 | -------------------------------------------------------------------------------- /src/dcli/src/output.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::str::FromStr; 24 | 25 | #[derive(Eq, PartialEq, Clone, Copy, Debug)] 26 | pub enum Output { 27 | Tsv, 28 | Default, 29 | } 30 | 31 | impl FromStr for Output { 32 | type Err = &'static str; 33 | 34 | fn from_str(s: &str) -> Result { 35 | //wrap in String so we can convert to lower case 36 | let s = String::from(s).to_lowercase(); 37 | 38 | //get a slice to get a &str for the match 39 | match &s[..] { 40 | "tsv" => Ok(Output::Tsv), 41 | "default" => Ok(Output::Default), 42 | _ => Err("Unknown Output type"), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/dcli/src/playeractivitiessummary.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #[derive(sqlx::FromRow, Debug)] 24 | pub struct PlayerActivitiesSummary { 25 | pub total_activities: u32, 26 | pub time_played_seconds: u32, 27 | pub wins: u32, 28 | pub completion_reason_mercy: u32, 29 | pub completed: u32, 30 | pub assists: u32, 31 | pub kills: u32, 32 | pub deaths: u32, 33 | pub opponents_defeated: u32, 34 | 35 | pub grenade_kills: u32, 36 | pub melee_kills: u32, 37 | pub super_kills: u32, 38 | pub ability_kills: u32, 39 | pub precision: u32, 40 | pub highest_assists: u32, 41 | pub highest_kills: u32, 42 | pub highest_deaths: u32, 43 | pub highest_opponents_defeated: u32, 44 | pub highest_grenade_kills: u32, 45 | pub highest_melee_kills: u32, 46 | pub highest_super_kills: u32, 47 | pub highest_ability_kills: u32, 48 | 49 | pub highest_kills_deaths_assists_ratio: f32, 50 | pub highest_kills_deaths_ratio: f32, 51 | pub highest_efficiency: f32, 52 | } 53 | -------------------------------------------------------------------------------- /src/dcli/src/response/activities.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use serde_derive::{Deserialize, Serialize}; 3 | 4 | use crate::enums::mode::Mode; 5 | use crate::enums::platform::Platform; 6 | use crate::response::drs::{DestinyResponseStatus, IsDestinyAPIResponse}; 7 | use crate::response::utils::str_to_datetime; 8 | use crate::response::utils::{ 9 | property_to_u32_value, property_to_value, standing_default, string_to_i64, 10 | }; 11 | 12 | pub const MAX_ACTIVITIES_REQUEST_COUNT: i32 = 250; 13 | 14 | //https://bungie-net.github.io/multi/operation_get_Destiny2-GetActivityHistory.html#operation_get_Destiny2-GetActivityHistory 15 | #[derive(Serialize, Deserialize, Debug)] 16 | pub struct ActivitiesResponse { 17 | #[serde(rename = "Response")] 18 | pub response: Option, 19 | 20 | #[serde(flatten)] 21 | pub status: DestinyResponseStatus, 22 | } 23 | 24 | impl IsDestinyAPIResponse for ActivitiesResponse { 25 | fn get_status(&self) -> &DestinyResponseStatus { 26 | &self.status 27 | } 28 | } 29 | 30 | //https://bungie-net.github.io/multi/schema_Destiny-HistoricalStats-DestinyActivityHistoryResults.html#schema_Destiny-HistoricalStats-DestinyActivityHistoryResults 31 | #[derive(Serialize, Deserialize, Debug)] 32 | pub struct ActivitiesResponseData { 33 | #[serde(rename = "activities")] 34 | pub activities: Option>, 35 | } 36 | 37 | //https://bungie-net.github.io/multi/schema_Destiny-HistoricalStats-DestinyHistoricalStatsPeriodGroup.html#schema_Destiny-HistoricalStats-DestinyHistoricalStatsPeriodGroup 38 | #[derive(Serialize, Deserialize, Debug)] 39 | pub struct Activity { 40 | #[serde(skip_serializing, deserialize_with = "str_to_datetime")] 41 | pub period: DateTime, 42 | 43 | #[serde(rename = "activityDetails")] 44 | pub details: DestinyHistoricalStatsActivity, 45 | 46 | //todo: can we collapse these down? 47 | pub values: ActivityHistoricalStatsValues, 48 | } 49 | 50 | #[derive(Serialize, Deserialize, Debug, Clone)] 51 | pub struct ActivityHistoricalStatsValues { 52 | #[serde(deserialize_with = "property_to_value")] 53 | pub assists: f32, 54 | 55 | #[serde(deserialize_with = "property_to_value")] 56 | pub score: f32, 57 | 58 | #[serde(deserialize_with = "property_to_value")] 59 | pub kills: f32, 60 | 61 | #[serde(deserialize_with = "property_to_value")] 62 | pub deaths: f32, 63 | 64 | #[serde( 65 | rename = "averageScorePerKill", 66 | deserialize_with = "property_to_value" 67 | )] 68 | #[serde(default)] 69 | pub average_score_per_kill: f32, 70 | 71 | #[serde( 72 | rename = "averageScorePerLife", 73 | deserialize_with = "property_to_value" 74 | )] 75 | #[serde(default)] 76 | pub average_score_per_life: f32, 77 | 78 | #[serde(deserialize_with = "property_to_value")] 79 | pub completed: f32, 80 | 81 | #[serde( 82 | rename = "opponentsDefeated", 83 | deserialize_with = "property_to_value" 84 | )] 85 | pub opponents_defeated: f32, 86 | 87 | #[serde(deserialize_with = "property_to_value")] 88 | pub efficiency: f32, 89 | 90 | #[serde( 91 | rename = "killsDeathsRatio", 92 | deserialize_with = "property_to_value" 93 | )] 94 | pub kills_deaths_ratio: f32, 95 | 96 | #[serde( 97 | rename = "killsDeathsAssists", 98 | deserialize_with = "property_to_value" 99 | )] 100 | pub kills_deaths_assists: f32, 101 | 102 | #[serde( 103 | rename = "activityDurationSeconds", 104 | deserialize_with = "property_to_value" 105 | )] 106 | pub activity_duration_seconds: f32, 107 | //TODO: need to make this an option 108 | #[serde(deserialize_with = "property_to_u32_value")] 109 | #[serde(default = "standing_default")] 110 | pub standing: u32, 111 | 112 | #[serde(deserialize_with = "property_to_value")] 113 | #[serde(default)] 114 | pub team: f32, 115 | 116 | #[serde( 117 | rename = "completionReason", 118 | deserialize_with = "property_to_value" 119 | )] 120 | pub completion_reason: f32, 121 | 122 | #[serde(rename = "startSeconds", deserialize_with = "property_to_value")] 123 | pub start_seconds: f32, 124 | 125 | #[serde( 126 | rename = "timePlayedSeconds", 127 | deserialize_with = "property_to_value" 128 | )] 129 | pub time_played_seconds: f32, 130 | 131 | #[serde(rename = "playerCount", deserialize_with = "property_to_value")] 132 | pub player_count: f32, 133 | 134 | #[serde(rename = "teamScore", deserialize_with = "property_to_value")] 135 | pub team_score: f32, 136 | 137 | #[serde(rename = "fireteamId", deserialize_with = "property_to_value")] 138 | pub fireteam_id: f64, 139 | } 140 | 141 | //https://bungie-net.github.io/multi/schema_Destiny-HistoricalStats-DestinyHistoricalStatsActivity.html#schema_Destiny-HistoricalStats-DestinyHistoricalStatsActivity 142 | #[derive(Serialize, Deserialize, Debug)] 143 | pub struct DestinyHistoricalStatsActivity { 144 | /// The unique hash identifier of the DestinyActivityDefinition that was played. 145 | /// (Seems to be the same as director_activity_hash) 146 | #[serde(rename = "referenceId")] 147 | pub reference_id: u32, 148 | 149 | /// The unique hash identifier of the DestinyActivityDefinition (Manifest) that was played 150 | #[serde(rename = "directorActivityHash")] 151 | pub director_activity_hash: u32, 152 | 153 | /// The unique identifier for this *specific* match that was played. 154 | /// 155 | /// This value can be used to get additional data about this activity such 156 | /// as who else was playing via the GetPostGameCarnageReport endpoint. 157 | #[serde(rename = "instanceId", deserialize_with = "string_to_i64")] 158 | pub instance_id: i64, 159 | 160 | pub mode: Mode, 161 | 162 | pub modes: Vec, //may need to make Option? 163 | 164 | /// Whether or not the match was a private match 165 | #[serde(rename = "isPrivate")] 166 | pub is_private: bool, 167 | 168 | /// The platform the activity was played on 169 | #[serde(rename = "membershipType")] 170 | pub membership_type: Platform, 171 | } 172 | -------------------------------------------------------------------------------- /src/dcli/src/response/character.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use chrono::{DateTime, Utc}; 24 | use serde_derive::{Deserialize, Serialize}; 25 | 26 | use crate::enums::character::{CharacterClass, CharacterGender, CharacterRace}; 27 | 28 | use crate::response::utils::str_to_int; 29 | 30 | use crate::emblem::Emblem; 31 | use crate::response::utils::str_to_datetime; 32 | 33 | use crate::response::utils::string_to_i64; 34 | 35 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 36 | pub struct CharacterData { 37 | #[serde(rename = "characterId", deserialize_with = "string_to_i64")] 38 | pub id: i64, 39 | 40 | #[serde( 41 | rename = "dateLastPlayed", 42 | skip_serializing, 43 | deserialize_with = "str_to_datetime" 44 | )] 45 | pub date_last_played: DateTime, //TODO: parse 2020-10-05T18:49:25Z 46 | 47 | #[serde(rename = "minutesPlayedTotal", deserialize_with = "str_to_int")] 48 | pub minutes_played_total: u32, 49 | 50 | #[serde(rename = "raceType")] 51 | pub race: CharacterRace, 52 | 53 | #[serde(rename = "classType")] 54 | pub class_type: CharacterClass, 55 | 56 | #[serde(rename = "genderType")] 57 | pub gender: CharacterGender, 58 | 59 | #[serde(rename = "emblemHash")] 60 | pub emblem_hash: u32, //TODO: check int type 61 | 62 | #[serde(rename = "baseCharacterLevel")] 63 | pub base_character_level: u32, 64 | 65 | #[serde(skip)] 66 | pub emblem: Option, 67 | 68 | pub stats: CharacterStatsData, 69 | } 70 | 71 | #[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] 72 | pub struct CharacterStatsData { 73 | #[serde(rename = "1935470627")] 74 | pub power: i32, 75 | 76 | #[serde(rename = "2996146975")] 77 | pub mobility: i32, 78 | 79 | #[serde(rename = "392767087")] 80 | pub resilience: i32, 81 | 82 | #[serde(rename = "1943323491")] 83 | pub recovery: i32, 84 | 85 | #[serde(rename = "1735777505")] 86 | pub discipline: i32, 87 | 88 | #[serde(rename = "144602215")] 89 | pub intellect: i32, 90 | 91 | #[serde(rename = "4244567218")] 92 | pub strength: i32, 93 | } 94 | -------------------------------------------------------------------------------- /src/dcli/src/response/cr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use serde_derive::{Deserialize, Serialize}; 24 | 25 | use crate::response::character::CharacterData; 26 | use crate::response::drs::{DestinyResponseStatus, IsDestinyAPIResponse}; 27 | 28 | #[derive(Serialize, Deserialize, Debug)] 29 | pub struct GetCharacterResponse { 30 | #[serde(rename = "Response")] 31 | pub response: Option, //should this be an option? 32 | 33 | #[serde(flatten)] 34 | pub status: DestinyResponseStatus, 35 | } 36 | 37 | impl IsDestinyAPIResponse for GetCharacterResponse { 38 | fn get_status(&self) -> &DestinyResponseStatus { 39 | &self.status 40 | } 41 | } 42 | 43 | #[derive(Serialize, Deserialize, Debug)] 44 | pub struct CharacterResponse { 45 | pub character: Option, 46 | } 47 | 48 | #[derive(Serialize, Deserialize, Debug)] 49 | pub struct CharacterDataFieldData { 50 | pub data: Option, 51 | } 52 | -------------------------------------------------------------------------------- /src/dcli/src/response/drs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use serde_derive::{Deserialize, Serialize}; 24 | 25 | use crate::error::Error; 26 | 27 | pub const API_RESPONSE_STATUS_SUCCESS: u32 = 1; 28 | 29 | pub fn check_destiny_response_status( 30 | status: &DestinyResponseStatus, 31 | ) -> Result<(), Error> { 32 | match status.error_code { 33 | 1 => Ok(()), 34 | 5 => Err(Error::ApiNotAvailableException), 35 | 7 => Err(Error::ParameterParseFailure), 36 | 18 => Err(Error::InvalidParameters), 37 | 1665 => Err(Error::PrivacyException), 38 | 2102 => Err(Error::ApiKeyMissingFromRequest), 39 | _ => Err(Error::ApiStatus { 40 | description: format!( 41 | "Response Status Error : {}({}) : {}", 42 | status.error_status, status.error_code, status.message 43 | ), 44 | }), 45 | } 46 | } 47 | 48 | #[derive(Serialize, Deserialize, Debug)] 49 | pub struct DestinyResponseStatus { 50 | #[serde(rename = "ErrorCode")] 51 | pub error_code: u32, 52 | 53 | #[serde(rename = "ThrottleSeconds")] 54 | pub throttle_seconds: u32, 55 | 56 | #[serde(rename = "ErrorStatus")] 57 | pub error_status: String, 58 | 59 | #[serde(rename = "Message")] 60 | pub message: String, 61 | } 62 | 63 | pub trait IsDestinyAPIResponse { 64 | fn get_status(&self) -> &DestinyResponseStatus; 65 | } 66 | -------------------------------------------------------------------------------- /src/dcli/src/response/ggms.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use crate::response::drs::{DestinyResponseStatus, IsDestinyAPIResponse}; 24 | use crate::response::pgcr::UserInfoCard; 25 | use serde_derive::{Deserialize, Serialize}; 26 | 27 | #[derive(Serialize, Deserialize, Debug)] 28 | pub struct GetGroupMemberResponse { 29 | #[serde(rename = "Response")] 30 | pub response: Option, //should this be an option? 31 | 32 | #[serde(flatten)] 33 | pub status: DestinyResponseStatus, 34 | } 35 | 36 | impl IsDestinyAPIResponse for GetGroupMemberResponse { 37 | fn get_status(&self) -> &DestinyResponseStatus { 38 | &self.status 39 | } 40 | } 41 | 42 | #[derive(Serialize, Deserialize, Debug)] 43 | pub struct GroupMemberResponse { 44 | pub results: Vec, 45 | } 46 | 47 | #[derive(Serialize, Deserialize, Debug)] 48 | pub struct GroupMemberInfo { 49 | #[serde(rename = "destinyUserInfo")] 50 | pub destiny_user_info: UserInfoCard, 51 | } 52 | -------------------------------------------------------------------------------- /src/dcli/src/response/gmd.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | drs::{DestinyResponseStatus, IsDestinyAPIResponse}, 3 | pgcr::UserInfoCard, 4 | }; 5 | use serde_derive::{Deserialize, Serialize}; 6 | 7 | #[derive(Serialize, Deserialize, Debug)] 8 | pub struct GetMembershipData { 9 | #[serde(rename = "Response")] 10 | pub response: Option, //should this be an option? 11 | 12 | #[serde(flatten)] 13 | pub status: DestinyResponseStatus, 14 | } 15 | 16 | #[derive(Serialize, Deserialize, Debug)] 17 | pub struct UserMembershipData { 18 | #[serde(rename = "destinyMemberships")] 19 | pub destiny_memberships: Vec, 20 | } 21 | 22 | impl IsDestinyAPIResponse for GetMembershipData { 23 | fn get_status(&self) -> &DestinyResponseStatus { 24 | &self.status 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/dcli/src/response/gpr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::collections::HashMap; 24 | 25 | use chrono::{DateTime, Utc}; 26 | use serde_derive::{Deserialize, Serialize}; 27 | 28 | use crate::enums::mode::Mode; 29 | use crate::response::character::CharacterData; 30 | use crate::response::drs::{DestinyResponseStatus, IsDestinyAPIResponse}; 31 | use crate::response::utils::str_to_datetime; 32 | 33 | use super::pgcr::UserInfoCard; 34 | 35 | #[derive(Serialize, Deserialize, Debug)] 36 | pub struct GetProfileResponse { 37 | #[serde(rename = "Response")] 38 | pub response: Option, //should this be an option? 39 | 40 | #[serde(flatten)] 41 | pub status: DestinyResponseStatus, 42 | } 43 | 44 | impl IsDestinyAPIResponse for GetProfileResponse { 45 | fn get_status(&self) -> &DestinyResponseStatus { 46 | &self.status 47 | } 48 | } 49 | 50 | #[derive(Serialize, Deserialize, Debug)] 51 | pub struct ProfileResponse { 52 | pub characters: Option, 53 | 54 | #[serde(rename = "characterActivities")] 55 | pub character_activities: Option, 56 | 57 | pub profile: Option, 58 | } 59 | 60 | #[derive(Serialize, Deserialize, Debug)] 61 | pub struct ProfileData { 62 | pub data: ProfileDetailsData, 63 | } 64 | 65 | #[derive(Serialize, Deserialize, Debug)] 66 | pub struct ProfileDetailsData { 67 | #[serde(rename = "userInfo")] 68 | pub user_info: UserInfoCard, 69 | } 70 | 71 | #[derive(Serialize, Deserialize, Debug)] 72 | pub struct CharacterActivitiesDataField { 73 | pub data: HashMap, 74 | } 75 | 76 | #[derive(Serialize, Deserialize, Debug)] 77 | pub struct CharactersDataFieldData { 78 | pub data: HashMap, 79 | } 80 | 81 | #[derive(Serialize, Deserialize, Debug)] 82 | pub struct CharacterActivitiesFieldData { 83 | pub data: HashMap, 84 | } 85 | 86 | #[derive(Serialize, Deserialize, Debug, Clone)] 87 | pub struct CharacterActivitiesData { 88 | #[serde( 89 | rename = "dateActivityStarted", 90 | skip_serializing, 91 | deserialize_with = "str_to_datetime" 92 | )] 93 | pub date_activity_started: DateTime, 94 | 95 | #[serde(rename = "currentActivityHash")] 96 | pub current_activity_hash: u32, 97 | 98 | #[serde(rename = "currentActivityModeHash")] 99 | pub current_activity_mode_hash: u32, //these both point to the same data (0 if not active) 100 | 101 | #[serde(rename = "currentActivityModeType")] 102 | //todo: could default this to none / 0 103 | pub current_activity_mode_type: Option, // (0 if not active) 104 | 105 | #[serde(rename = "currentPlaylistActivityHash")] 106 | pub current_playlist_activity_hash: Option, //how is this different than currentActivityHash? 107 | } 108 | 109 | /* 110 | "currentActivityHash": 1813752023, //destination (will be 0 if not active) 111 | "currentActivityModeHash": 3497767639, //activity (will be 0 if not active) 112 | "currentActivityModeType": 6, //activity (patrol) 113 | "currentActivityModeHashes": [ 114 | 3497767639, //activty 115 | 1164760493 //pve 116 | ], 117 | "currentActivityModeTypes": [ 118 | 6, //patrol 119 | 7 //AllPVE 120 | ], 121 | "currentPlaylistActivityHash": 1813752023, //destination 122 | "lastCompletedStoryHash": 0 123 | */ 124 | -------------------------------------------------------------------------------- /src/dcli/src/response/manifest.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use serde_derive::{Deserialize, Serialize}; 24 | 25 | use crate::response::drs::{DestinyResponseStatus, IsDestinyAPIResponse}; 26 | use crate::response::utils::prepend_base_url; 27 | 28 | #[derive(Serialize, Deserialize, Debug)] 29 | pub struct ManifestResponse { 30 | #[serde(rename = "Response")] 31 | pub response: Option, 32 | 33 | #[serde(flatten)] 34 | pub status: DestinyResponseStatus, 35 | } 36 | 37 | impl IsDestinyAPIResponse for ManifestResponse { 38 | fn get_status(&self) -> &DestinyResponseStatus { 39 | &self.status 40 | } 41 | } 42 | 43 | #[derive(Serialize, Deserialize, Debug)] 44 | pub struct ManifestData { 45 | pub version: String, 46 | 47 | #[serde(rename = "mobileWorldContentPaths")] 48 | pub mobile_world_content_paths: MobileWorldContentPathsData, 49 | } 50 | 51 | #[derive(Serialize, Deserialize, Debug)] 52 | pub struct MobileWorldContentPathsData { 53 | #[serde(deserialize_with = "prepend_base_url")] 54 | pub en: String, 55 | } 56 | -------------------------------------------------------------------------------- /src/dcli/src/response/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | pub mod activities; 24 | pub mod character; 25 | pub mod cr; 26 | pub mod drs; 27 | pub mod ggms; 28 | pub mod gmd; 29 | pub mod gpr; 30 | pub mod manifest; 31 | pub mod pgcr; 32 | pub mod sdpr; 33 | pub mod stats; 34 | pub mod utils; 35 | -------------------------------------------------------------------------------- /src/dcli/src/response/sdpr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use serde_derive::{Deserialize, Serialize}; 24 | 25 | use super::{ 26 | drs::{DestinyResponseStatus, IsDestinyAPIResponse}, 27 | pgcr::{DestinyProfileUserInfoCard, UserInfoCard}, 28 | }; 29 | 30 | #[derive(Serialize, Debug)] 31 | pub struct SearchDestinyPlayerPostData { 32 | #[serde(rename = "displayName")] 33 | pub display_name: String, 34 | 35 | #[serde(rename = "displayNameCode")] 36 | pub display_name_code: String, 37 | } 38 | 39 | impl SearchDestinyPlayerPostData { 40 | pub fn to_json(&self) -> Result { 41 | let out = serde_json::to_string(&self)?; 42 | Ok(out) 43 | } 44 | } 45 | 46 | #[derive(Serialize, Deserialize, Debug)] 47 | pub struct SearchDestinyPlayerResponse { 48 | #[serde(rename = "Response")] 49 | pub response: Option>, 50 | 51 | #[serde(flatten)] 52 | pub status: DestinyResponseStatus, 53 | } 54 | 55 | impl IsDestinyAPIResponse for SearchDestinyPlayerResponse { 56 | fn get_status(&self) -> &DestinyResponseStatus { 57 | &self.status 58 | } 59 | } 60 | 61 | #[derive(Serialize, Deserialize, Debug)] 62 | pub struct LinkedProfilesResponse { 63 | #[serde(rename = "Response")] 64 | pub response: Option, 65 | 66 | #[serde(flatten)] 67 | pub status: DestinyResponseStatus, 68 | } 69 | 70 | impl IsDestinyAPIResponse for LinkedProfilesResponse { 71 | fn get_status(&self) -> &DestinyResponseStatus { 72 | &self.status 73 | } 74 | } 75 | 76 | #[derive(Serialize, Deserialize, Debug)] 77 | pub struct DestinyLinkedProfilesResponse { 78 | pub profiles: Vec, 79 | } 80 | -------------------------------------------------------------------------------- /src/dcli/src/response/utils.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use chrono::{DateTime, NaiveDateTime, Utc}; 24 | use serde::Deserialize; 25 | //use serde_derive::Deserialize; 26 | 27 | use crate::apiutils::RESOURCE_BASE_URL; 28 | use crate::enums::standing::STANDING_UNKNOWN_MAGIC_NUMBER; 29 | 30 | use std::fmt::Display; 31 | use std::str::FromStr; 32 | 33 | //2020-10-05T18:49:25Z 34 | pub const API_DATE_TIME_FORMAT: &str = "%Y-%m-%dT%H:%M:%SZ"; 35 | 36 | pub fn property_to_i32_value<'de, D>(deserializer: D) -> Result 37 | where 38 | D: serde::de::Deserializer<'de>, 39 | { 40 | #[derive(serde_derive::Deserialize)] 41 | struct Outer { 42 | pub basic: Inner, 43 | } 44 | 45 | #[derive(serde_derive::Deserialize)] 46 | struct Inner { 47 | pub value: f32, 48 | } 49 | 50 | let helper = ::deserialize(deserializer)?; 51 | Ok(helper.basic.value as i32) 52 | } 53 | 54 | pub fn property_to_u32_value<'de, D>(deserializer: D) -> Result 55 | where 56 | D: serde::de::Deserializer<'de>, 57 | { 58 | #[derive(serde_derive::Deserialize)] 59 | struct Outer { 60 | pub basic: Inner, 61 | } 62 | 63 | #[derive(serde_derive::Deserialize)] 64 | struct Inner { 65 | pub value: f32, 66 | } 67 | 68 | let helper = ::deserialize(deserializer)?; 69 | Ok(helper.basic.value as u32) 70 | } 71 | 72 | pub fn property_to_value<'de, D, T: serde::de::Deserialize<'de>>( 73 | deserializer: D, 74 | ) -> Result 75 | where 76 | D: serde::de::Deserializer<'de>, 77 | { 78 | #[derive(serde_derive::Deserialize)] 79 | struct Outer { 80 | pub basic: Inner, 81 | } 82 | 83 | #[derive(serde_derive::Deserialize)] 84 | struct Inner { 85 | pub value: T, 86 | } 87 | 88 | let helper = >::deserialize(deserializer)?; 89 | Ok(helper.basic.value) 90 | } 91 | 92 | /* 93 | pub fn property_to_standing<'de, D>(deserializer: D) -> Result 94 | where 95 | D: serde::de::Deserializer<'de>, 96 | { 97 | #[derive(serde_derive::Deserialize)] 98 | struct Outer { 99 | pub basic: Inner, 100 | } 101 | 102 | #[derive(serde_derive::Deserialize)] 103 | struct Inner { 104 | pub value: f32, 105 | } 106 | 107 | let helper = Outer::deserialize(deserializer)?; 108 | Ok(Standing::from_f32(helper.basic.value)) 109 | } 110 | */ 111 | 112 | //BUG: this doesnt get called if the property is not include in the JSON 113 | //https://github.com/serde-rs/json/issues/734 114 | pub fn property_to_option_float<'de, D>( 115 | deserializer: D, 116 | ) -> Result, D::Error> 117 | where 118 | D: serde::de::Deserializer<'de>, 119 | { 120 | #[derive(serde_derive::Deserialize, Debug)] 121 | struct Outer { 122 | pub basic: Inner, 123 | } 124 | 125 | #[derive(serde_derive::Deserialize, Debug)] 126 | struct Inner { 127 | pub value: f32, 128 | } 129 | 130 | Option::::deserialize(deserializer).map(|o: Option| match o { 131 | Some(e) => Some(e.basic.value), 132 | None => None, 133 | }) 134 | } 135 | 136 | /* 137 | Option::::deserialize(deserializer).map(|o: Option| match o { 138 | Some(e) => { 139 | let mut s = String::from(RESOURCE_BASE_URL); 140 | s.push_str(&e); 141 | Some(s) 142 | } 143 | None => None, 144 | }) 145 | */ 146 | 147 | pub fn prepend_base_url<'de, D>(deserializer: D) -> Result 148 | where 149 | D: serde::de::Deserializer<'de>, 150 | { 151 | String::deserialize(deserializer).map(|a| { 152 | let mut s = String::from(RESOURCE_BASE_URL); 153 | s.push_str(&a); 154 | s 155 | }) 156 | } 157 | 158 | pub fn prepend_base_url_option<'de, D>( 159 | deserializer: D, 160 | ) -> Result, D::Error> 161 | where 162 | D: serde::de::Deserializer<'de>, 163 | { 164 | Option::::deserialize(deserializer).map(|o: Option| match o 165 | { 166 | Some(e) => { 167 | let mut s = String::from(RESOURCE_BASE_URL); 168 | s.push_str(&e); 169 | Some(s) 170 | } 171 | None => None, 172 | }) 173 | } 174 | 175 | pub fn string_to_i64<'de, D>(deserializer: D) -> Result 176 | where 177 | D: serde::de::Deserializer<'de>, 178 | { 179 | let s = String::deserialize(deserializer)?; 180 | 181 | let out = match s.parse::() { 182 | Ok(e) => e, 183 | Err(e) => { 184 | return Err(serde::de::Error::custom(&format!( 185 | "Could not parse string to i64 : {}", 186 | e 187 | ))) 188 | } 189 | }; 190 | 191 | Ok(out) 192 | } 193 | 194 | //str_to_datetime 195 | pub fn str_to_datetime<'de, D>( 196 | deserializer: D, 197 | ) -> Result, D::Error> 198 | where 199 | D: serde::de::Deserializer<'de>, 200 | { 201 | let s = String::deserialize(deserializer)?; 202 | 203 | let n = match NaiveDateTime::parse_from_str(&s, API_DATE_TIME_FORMAT) { 204 | Ok(e) => e, 205 | Err(e) => { 206 | return Err(serde::de::Error::custom(&format!( 207 | "Could not parse date-time : {}", 208 | e 209 | ))) 210 | } 211 | }; 212 | 213 | let dt = DateTime::::from_utc(n, Utc); 214 | 215 | Ok(dt) 216 | } 217 | 218 | pub fn standing_default() -> u32 { 219 | STANDING_UNKNOWN_MAGIC_NUMBER 220 | } 221 | 222 | pub fn str_to_int<'de, T, D>(deserializer: D) -> Result 223 | where 224 | T: FromStr, 225 | T::Err: Display, 226 | D: serde::de::Deserializer<'de>, 227 | { 228 | let s = String::deserialize(deserializer)?; 229 | T::from_str(&s).map_err(serde::de::Error::custom) 230 | } 231 | -------------------------------------------------------------------------------- /src/dcli/src/statscontainer.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use crate::enums::standing::Standing; 24 | use crate::response::activities::Activity; 25 | use crate::utils::{ 26 | calculate_average, calculate_efficiency, calculate_kills_deaths_assists, 27 | calculate_kills_deaths_ratio, 28 | }; 29 | 30 | #[derive(Default)] 31 | pub struct ActivityStatsContainer { 32 | pub activities: Vec, 33 | 34 | assists: f32, 35 | score: f32, 36 | kills: f32, 37 | deaths: f32, 38 | opponents_defeated: f32, 39 | efficiency: f32, 40 | kills_deaths_ratio: f32, 41 | kills_deaths_assists: f32, 42 | wins: f32, 43 | losses: f32, 44 | draws: f32, 45 | time_played_seconds: f32, 46 | 47 | highest_kills: f32, 48 | highest_assists: f32, 49 | highest_deaths: f32, 50 | highest_opponents_defeated: f32, 51 | highest_efficiency: f32, 52 | highest_kills_deaths_ratio: f32, 53 | highest_kills_deaths_assists: f32, 54 | 55 | longest_win_streak: f32, 56 | longest_loss_streak: f32, 57 | } 58 | 59 | impl ActivityStatsContainer { 60 | pub fn with_activities( 61 | activities: Vec, 62 | ) -> ActivityStatsContainer { 63 | let mut a = ActivityStatsContainer { 64 | activities, 65 | assists: 0.0, 66 | score: 0.0, 67 | kills: 0.0, 68 | deaths: 0.0, 69 | opponents_defeated: 0.0, 70 | efficiency: 0.0, 71 | kills_deaths_ratio: 0.0, 72 | kills_deaths_assists: 0.0, 73 | wins: 0.0, 74 | losses: 0.0, 75 | draws: 0.0, 76 | time_played_seconds: 0.0, 77 | 78 | highest_kills: 0.0, 79 | highest_assists: 0.0, 80 | highest_deaths: 0.0, 81 | highest_opponents_defeated: 0.0, 82 | highest_efficiency: 0.0, 83 | highest_kills_deaths_ratio: 0.0, 84 | highest_kills_deaths_assists: 0.0, 85 | 86 | longest_win_streak: 0.0, 87 | longest_loss_streak: 0.0, 88 | }; 89 | 90 | a.update(); 91 | a 92 | } 93 | 94 | pub fn per_activity_average(&self, value: f32) -> f32 { 95 | calculate_average(value as u32, self.activities.len() as u32) 96 | } 97 | 98 | fn update(&mut self) { 99 | let mut last_standing = Standing::Unknown; 100 | let mut streak = 0.0; 101 | for a in self.activities.iter() { 102 | self.assists += a.values.assists; 103 | self.score += a.values.score; 104 | self.kills += a.values.kills; 105 | 106 | //self.highest_kills = max(self.highest_kills as u32, a.values.kills as u32); 107 | self.highest_kills = self.highest_kills.max(a.values.kills); 108 | self.highest_assists = self.highest_assists.max(a.values.assists); 109 | self.highest_deaths = self.highest_deaths.max(a.values.deaths); 110 | self.highest_opponents_defeated = self 111 | .highest_opponents_defeated 112 | .max(a.values.opponents_defeated); 113 | 114 | self.highest_efficiency = 115 | self.highest_efficiency.max(a.values.efficiency); 116 | self.highest_kills_deaths_ratio = self 117 | .highest_kills_deaths_ratio 118 | .max(a.values.kills_deaths_ratio); 119 | self.highest_kills_deaths_assists = self 120 | .highest_kills_deaths_assists 121 | .max(a.values.kills_deaths_assists); 122 | 123 | self.deaths += a.values.deaths; 124 | self.opponents_defeated += a.values.opponents_defeated; 125 | self.time_played_seconds += a.values.time_played_seconds; 126 | 127 | let standing = 128 | Standing::from_mode(a.values.standing, &a.details.mode); 129 | match standing { 130 | Standing::Victory => { 131 | self.wins += 1.0; 132 | } 133 | Standing::Defeat => { 134 | self.losses += 1.0; 135 | } 136 | Standing::Unknown => { 137 | self.draws += 1.0; 138 | } 139 | }; 140 | 141 | if standing == last_standing { 142 | streak = match last_standing { 143 | Standing::Unknown => 0.0, 144 | Standing::Victory => streak + 1.0, 145 | Standing::Defeat => streak - 1.0, 146 | }; 147 | } else { 148 | last_standing = standing; 149 | streak = match last_standing { 150 | Standing::Unknown => 0.0, 151 | Standing::Victory => 1.0, 152 | Standing::Defeat => -1.0, 153 | }; 154 | } 155 | 156 | self.longest_loss_streak = self.longest_loss_streak.min(streak); 157 | self.longest_win_streak = self.longest_win_streak.max(streak); 158 | } 159 | 160 | self.kills_deaths_assists = calculate_kills_deaths_assists( 161 | self.kills as u32, 162 | self.deaths as u32, 163 | self.assists as u32, 164 | ); 165 | self.kills_deaths_ratio = 166 | calculate_kills_deaths_ratio(self.kills as u32, self.deaths as u32); 167 | self.efficiency = calculate_efficiency( 168 | self.kills as u32, 169 | self.deaths as u32, 170 | self.assists as u32, 171 | ); 172 | } 173 | 174 | pub fn longest_win_streak(&self) -> f32 { 175 | self.longest_win_streak 176 | } 177 | 178 | pub fn longest_loss_streak(&self) -> f32 { 179 | self.longest_loss_streak.abs() 180 | } 181 | 182 | pub fn win_percentage(&self) -> f32 { 183 | let total = self.total_activities(); 184 | 185 | if total == 0.0 { 186 | return 0.0; 187 | } 188 | 189 | self.wins / total * 100.0 190 | } 191 | 192 | pub fn highest_efficiency(&self) -> f32 { 193 | self.highest_efficiency 194 | } 195 | 196 | pub fn highest_kills_deaths_assists(&self) -> f32 { 197 | self.highest_kills_deaths_assists 198 | } 199 | 200 | pub fn highest_kills_deaths_ratio(&self) -> f32 { 201 | self.highest_kills_deaths_ratio 202 | } 203 | 204 | pub fn highest_kills(&self) -> f32 { 205 | self.highest_kills 206 | } 207 | 208 | pub fn highest_deaths(&self) -> f32 { 209 | self.highest_deaths 210 | } 211 | 212 | pub fn highest_assists(&self) -> f32 { 213 | self.highest_assists 214 | } 215 | 216 | pub fn highest_opponents_defeated(&self) -> f32 { 217 | self.highest_opponents_defeated 218 | } 219 | 220 | pub fn assists(&self) -> f32 { 221 | self.assists 222 | } 223 | 224 | pub fn kills(&self) -> f32 { 225 | self.kills 226 | } 227 | 228 | pub fn deaths(&self) -> f32 { 229 | self.deaths 230 | } 231 | 232 | pub fn opponents_defeated(&self) -> f32 { 233 | self.opponents_defeated 234 | } 235 | 236 | pub fn efficiency(&self) -> f32 { 237 | self.efficiency 238 | } 239 | 240 | pub fn kills_deaths_ratio(&self) -> f32 { 241 | self.kills_deaths_ratio 242 | } 243 | 244 | pub fn kills_deaths_assists(&self) -> f32 { 245 | self.kills_deaths_assists 246 | } 247 | 248 | pub fn wins(&self) -> f32 { 249 | self.wins 250 | } 251 | 252 | pub fn losses(&self) -> f32 { 253 | self.losses 254 | } 255 | 256 | pub fn draws(&self) -> f32 { 257 | self.draws 258 | } 259 | 260 | pub fn total_activities(&self) -> f32 { 261 | self.activities.len() as f32 262 | } 263 | } 264 | 265 | //create a trait for the stats type that has getters for all shared stats 266 | -------------------------------------------------------------------------------- /src/dclia/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dclia" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | description = "Command line tool for retrieving information on current activity for specified player character." 7 | homepage = "https://www.mikechambers.com" 8 | repository = "https://github.com/mikechambers/dcli" 9 | edition = "2018" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | log = "0.4.17" 19 | env_logger = "0.9.3" 20 | 21 | tell = { path = "../tell/"} 22 | dcli = { path = "../dcli/"} 23 | -------------------------------------------------------------------------------- /src/dclia/README.md: -------------------------------------------------------------------------------- 1 | # dclia 2 | 3 | Command line tool for retrieving current Destiny 2 activity status for players. 4 | 5 | Information includes current activity, location, and in the case of PvP modes (Crucible and Gambit), current map. 6 | 7 | The API updates pretty quickly, and can be used to see check the activity and / or map while you are loading in. 8 | 9 | The too expects that the manifest has been downloaded and synced to the default location using [dclim](https://github.com/mikechambers/dcli/tree/main/src/dclim). You can specify a custom path to the manifest using the --data-dir argument. 10 | 11 | ## USAGE 12 | 13 | ``` 14 | USAGE: 15 | dclia [FLAGS] [OPTIONS] --name 16 | 17 | FLAGS: 18 | -h, --help 19 | Prints help information 20 | 21 | -V, --version 22 | Prints version information 23 | 24 | -v, --verbose 25 | Print out additional information 26 | 27 | OPTIONS: 28 | -k, --api-key 29 | API key from Bungie required for some actions. 30 | 31 | If specified the key will be passed to all Destiny API calls. 32 | 33 | You can obtain a key from https://www.bungie.net/en/Application [env: 34 | DESTINY_API_KEY=8eacb6527ea648fbbd8106990231c21c] 35 | -D, --data-dir 36 | Directory where Destiny 2 manifest database file is stored. (optional) 37 | 38 | This will normally be downloaded using the dclim tool, and stored in a file named manifest.sqlite3 (in the 39 | manifest directory specified when running dclim). 40 | -n, --name 41 | Bungie name for player 42 | 43 | Name must be in the format of NAME#CODE. Example: foo#3280 You can find your name in game, or on Bungie's 44 | site at: https://www.bungie.net/7/en/User/Account/IdentitySettings 45 | -O, --output-format 46 | Format for command output 47 | 48 | Valid values are default (Default) and tsv. 49 | 50 | tsv outputs in a tab (\t) separated format of name / value pairs with lines ending in a new line character 51 | (\n). [default: default] 52 | 53 | ``` 54 | 55 | Manifest can be downloaded and synced with from [dclim](https://github.com/mikechambers/dcli/tree/main/src/dclim). 56 | 57 | ### Examples 58 | 59 | #### Check for current activity. 60 | 61 | ``` 62 | $ dclia --name mesh#3230 63 | ``` 64 | 65 | outputs: 66 | 67 | ``` 68 | Playing Deep Stone Crypt Raid on Castalia Macula, Europa 69 | ``` 70 | 71 | #### Check for current activity with tab separated output: 72 | 73 | ``` 74 | $ dclia --name mesh#3230 --output-format tsv 75 | ``` 76 | 77 | outputs: 78 | 79 | ``` 80 | in_activity true 81 | activity_type_name Strike 82 | activity_name The Inverted Spire 83 | place_name Nessus 84 | destination_name Arcadian Valley 85 | description End the Red Legion expedition that's ripped open the planet's surface. 86 | human_status Running The Inverted Spire Strike on Nessus 87 | is_crucible false 88 | ``` 89 | 90 | ## Questions, Feature Requests, Feedback 91 | 92 | If you have any questions, feature requests, need help, are running into issues, or just want to chat, join the [dcli Discord server](https://discord.gg/2Y8bV2Mq3p). 93 | 94 | You can also log bugs and features requests on the [issues page](https://github.com/mikechambers/dcli/issues). 95 | 96 | ### Environment Variables 97 | 98 | #### DCLI_FIX_DATA 99 | 100 | If the `DCLI_FIX_DATA` environment variable is set to `TRUE` then when corrupt or missing data is returned from the Bungie API, and there is not a valid local version, DCLI will attempt to retrieve updated, non-corrupt data from Bungie. (This sometimes happens if a lot of people leave a game, and no player names will be returned from the server). 101 | 102 | Setting this to true can significantly slow down sync time, especially the initial sync, and in general, is meant to be used when using DCLI to create datastores for larger applications. 103 | 104 | #### RUST_LOG 105 | 106 | All dcli apps have support for log output via the [env_logger](https://docs.rs/env_logger/0.9.3/env_logger/) library. This is mostly used for development, but may be helpful when trying to debug any issues. 107 | 108 | ## Compiling 109 | 110 | This utility is written and compiled in [Rust](https://www.rust-lang.org/). 111 | 112 | When compiling you must have an environment variable named `DESTINY_API_KEY` which contains your [Bungie API key](https://www.bungie.net/en/Application). 113 | 114 | To compile, switch to the `src/` directory and run: 115 | 116 | ``` 117 | $ cargo build --release 118 | ``` 119 | 120 | which will place the compiled tools in _src/target/release_ 121 | -------------------------------------------------------------------------------- /src/dcliad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dcliad" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Command line tool for viewing Destiny 2 activity details." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | log = "0.4.17" 19 | env_logger = "0.9.3" 20 | 21 | dcli = { path = "../dcli/"} 22 | tell = { path = "../tell/"} 23 | -------------------------------------------------------------------------------- /src/dcliad/README.md: -------------------------------------------------------------------------------- 1 | # dcliad 2 | 3 | Command line tool for retrieving and viewing Destiny 2 Crucible activity / match details. 4 | 5 | By default the details on the last activity will be displayed, with options (`--mode`) to specify the mode from which to retrieve the last activity. 6 | 7 | You can also specify the specific activity via the `--activity-index` argument. The index can be retrieved from dcliah. 8 | 9 | By default, the app will display summary data for the match, including each player and an overview of weapon usage. By passing in the `--details` flag, per user weapon usage and stats will be displayed. 10 | 11 | dcliad pulls its data from the local Destiny 2 activity database store. Data can be synced using using [dclisync](https://github.com/mikechambers/dcli/tree/main/src/dclisync) or by passing the --sync flag to dcliad. 12 | 13 | The tool expects that the manifest has been downloaded and synced using [dclim](https://github.com/mikechambers/dcli/tree/main/src/dclim). 14 | 15 | [![Image of dcliah](../../images/dcliad_sm.png)](../../images/dcliad.png) 16 | 17 | ## USAGE 18 | 19 | ``` 20 | USAGE: 21 | dcliad [FLAGS] [OPTIONS] --name 22 | 23 | FLAGS: 24 | -d, --details 25 | Display extended activity details 26 | 27 | If flag is set, additional information will be displayed, including per user weapon stats. 28 | -h, --help 29 | Prints help information 30 | 31 | -s, --sync 32 | Sync activities for specified user 33 | 34 | -V, --version 35 | Prints version information 36 | 37 | -v, --verbose 38 | Print out additional information 39 | 40 | OPTIONS: 41 | -a, --activity-id 42 | The activity id of the activity to display data about 43 | 44 | By default, the last activity will be displayed. The index can be retrieved 45 | /// from other dcli apps, such as dcliah, or directly from the sqlite datastore. 46 | dcliah, or directly from the sqlite datastore. 47 | -k, --api-key 48 | API key from Bungie required for some actions. 49 | 50 | If specified the key will be passed to all Destiny API calls. 51 | 52 | You can obtain a key from https://www.bungie.net/en/Application [env: 53 | DESTINY_API_KEY=8eacb6527ea648fbbd8106990231c21c] 54 | -C, --class 55 | Character class to retrieve data for 56 | 57 | Valid values include hunter, titan, warlock, last_active and all. [default: all] 58 | -D, --data-dir 59 | Directory where Destiny 2 manifest and activity database files are stored. (optional) 60 | 61 | This will normally be downloaded using the dclim tool, and uses a system appropriate directory by default. 62 | -M, --mode 63 | Activity mode from which to return last activity 64 | 65 | Supported values are all_pvp (default), control, clash, elimination, mayhem, iron_banner, all_private, 66 | rumble, pvp_competitive, quickplay and trials_of_osiris. 67 | 68 | Addition values available are crimsom_doubles, supremacy, survival, countdown, all_doubles, doubles, 69 | private_clash, private_control, private_survival, private_rumble, showdown_competitive, survival_competitive, rift_competitive, showdown, lockdown, scorched, rift, iron_banner_rift, zone_control, iron_banner_zone_control 70 | scorched_team, breakthrough, clash_quickplay, trials_of_the_nine, relic, countdown_competitive, checkmate_all, checkmate_control, checkmate_rumble, checkmate_survival, checkmate_rumble, checkmate_clash, checkmate_countdown, collision_competitive, iron_banner_tribute, iron_banner_fortress [default: all_pvp] 71 | -n, --name 72 | Bungie name for player 73 | 74 | Name must be in the format of NAME#CODE. Example: foo#3280 You can find your name in game, or on Bungie's 75 | site at: https://www.bungie.net/7/en/User/Account/IdentitySettings 76 | -w, --weapon-count 77 | The number of weapons to display details for [default: 5] 78 | ``` 79 | 80 | | ARGUMENT | OPTIONS | 81 | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 82 | | --mode | all_pvp (default), control, clash, elimination, mayhem, iron_banner, all_private, rumble, pvp_competitive, quickplay and trials_of_osiris, crimsom_doubles, supremacy, survival, countdown, all_doubles, doubles private_clash, private_control, private_survival, private_rumble, showdown_competitive, survival_competitive, rift_competitive, showdown, lockdown, scorched, rift, iron_banner_rift, zone_control, iron_banner_zone_control, scorched_team, breakthrough, clash_quickplay, trials_of_the_nine, relic, countdown_competitive, checkmate_all, checkmate_control, checkmate_rumble, checkmate_survival, checkmate_rumble, checkmate_clash, checkmate_countdown, collision_competitive, iron_banner_tribute, iron_banner_fortress | 83 | 84 | Manifest can be downloaded and synced with from [dclim](https://github.com/mikechambers/dcli/tree/main/src/dclim). 85 | 86 | Activity data store can be created and synced seperately using [dclisync](https://github.com/mikechambers/dcli/tree/main/src/dclisync). 87 | 88 | **NOTE** : Currently, due to a [bug](https://github.com/Bungie-net/api/issues/1386) in the Destiny 2 API, you will only get results for private matches when specifying _all_private_. The other options are still included in case the bug is fixed. If viewing private match stats is important to you, please leave a comment [here](https://github.com/mikechambers/dcli/issues/10). 89 | 90 | ### Examples 91 | 92 | #### View details for last activity played 93 | 94 | ``` 95 | $ dcliad --name mesh#3230 96 | ``` 97 | 98 | #### View details for last Iron Banner match played on hunter class 99 | 100 | ``` 101 | $ dcliad --name mesh#3230 --mode iron_banner --class hunter 102 | ``` 103 | 104 | #### View details for last activity played displaying extended details 105 | 106 | ``` 107 | $ dcliad --name mesh#3230 --details 108 | ``` 109 | 110 | #### View details for a specific activity via its index (retrieved from dcliah) 111 | 112 | ``` 113 | $ dcliad --name mesh#3230 --activity-index 7329 114 | ``` 115 | 116 | ## Questions, Feature Requests, Feedback 117 | 118 | If you have any questions, feature requests, need help, are running into issues, or just want to chat, join the [dcli Discord server](https://discord.gg/2Y8bV2Mq3p). 119 | 120 | You can also log bugs and features requests on the [issues page](https://github.com/mikechambers/dcli/issues). 121 | 122 | ### Environment Variables 123 | 124 | #### DCLI_FIX_DATA 125 | 126 | If the `DCLI_FIX_DATA` environment variable is set to `TRUE` then when corrupt or missing data is returned from the Bungie API, and there is not a valid local version, DCLI will attempt to retrieve updated, non-corrupt data from Bungie. (This sometimes happens if a lot of people leave a game, and no player names will be returned from the server). 127 | 128 | Setting this to true can significantly slow down sync time, especially the initial sync, and in general, is meant to be used when using DCLI to create datastores for larger applications. 129 | 130 | #### RUST_LOG 131 | 132 | All dcli apps have support for log output via the [env_logger](https://docs.rs/env_logger/0.9.3/env_logger/) library. This is mostly used for development, but may be helpful when trying to debug any issues. 133 | 134 | ## Compiling 135 | 136 | This utility is written and compiled in [Rust](https://www.rust-lang.org/). 137 | 138 | When compiling you must have an environment variable named `DESTINY_API_KEY` which contains your [Bungie API key](https://www.bungie.net/en/Application). 139 | 140 | To compile, switch to the `src/` directory and run: 141 | 142 | ``` 143 | $ cargo build --release 144 | ``` 145 | 146 | which will place the compiled tools in _src/target/release_ 147 | -------------------------------------------------------------------------------- /src/dcliah/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dcliah" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Command line tool for viewing Destiny 2 activity history." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | chrono = "0.4.23" 19 | num-format = "0.4.0" 20 | log = "0.4.17" 21 | env_logger = "0.9.3" 22 | 23 | dcli = { path = "../dcli/"} 24 | tell = { path = "../tell/"} -------------------------------------------------------------------------------- /src/dclif/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dclif" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Command line tool for querying specific Destiny 2 pvp stats." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | chrono = "0.4.23" 19 | num-format = "0.4.0" 20 | log = "0.4.17" 21 | env_logger = "0.9.3" 22 | 23 | dcli = { path = "../dcli/"} 24 | tell = { path = "../tell/"} 25 | -------------------------------------------------------------------------------- /src/dclif/README.md: -------------------------------------------------------------------------------- 1 | ## Privacy 2 | 3 | Note, in order for dclisync and dclif to be able to retrieve your data, you must have the following two privacy options select on your Bungie account at [https://www.bungie.net/7/en/User/Account/Privacy](https://www.bungie.net/7/en/User/Account/Privacy) 4 | 5 | - Show my Destiny game Activity feed on Bungie.net 6 | - Show my progression 7 | -------------------------------------------------------------------------------- /src/dclif/src/classascii.rs: -------------------------------------------------------------------------------- 1 | pub struct ClassAscii { 2 | pub hunter: Vec, 3 | } 4 | 5 | impl ClassAscii { 6 | pub fn init() -> ClassAscii { 7 | let hunter = Vec::from([ 8 | " ".to_string(), 9 | " .=#%+: ".to_string(), 10 | " :*%@@@@@@@*- ".to_string(), 11 | " .=#@@@@@@@@@@@@@@*= ".to_string(), 12 | " :+%@@@@@@@@@@@@@@@@@@@@#=. ".to_string(), 13 | " .-#@@@@@@@@@@@@@@@@@@@@@@@@@@@%+: ".to_string(), 14 | " .*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ".to_string(), 15 | " :*= =#@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+:-*- ".to_string(), 16 | " :@@@%+: -*@@@@@@@@@@@@@@@@@@@@@@#-:=#@@@- ".to_string(), 17 | " :@@@@@@@*- :+@@@@@@@@@@@@@@@%+:-+%@@@@@@- ".to_string(), 18 | " :@@@@@@@@@@%=:.=%@@@@@@@@#=:=#@@@@@@@@@@- ".to_string(), 19 | " :@@@@@@@@@@@@@@*-.=#@%*-:+%@@@@@@@@@@@@@- ".to_string(), 20 | " :@@@@@@@@@@@@@@@@@+ =@@@@@@@@@@@@@@@@@- ".to_string(), 21 | " :@@@@@@@@@@@@@%*--+%@#=:-+@@@@@@@@@@@@@@- ".to_string(), 22 | " :@@@@@@@@@@#=--*@@@@@@@@%+:.=#@@@@@@@@@@- ".to_string(), 23 | " :@@@@@@@*=-=#@@@@@@@@@@@@@@@*-.-+@@@@@@@- ".to_string(), 24 | " :@@@#=:-*@@@@@@@@@@@@@@@@@@@@@@*= .=#@@@- ".to_string(), 25 | " .*-:=#@@@@@@@@@@@@@@@@@@@@@@@@@@@@#=. -*- ".to_string(), 26 | " =@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*. ".to_string(), 27 | " .=%@@@@@@@@@@@@@@@@@@@@@@@@@@@#=. ".to_string(), 28 | " =#@@@@@@@@@@@@@@@@@@@@%*- ".to_string(), 29 | " -*@@@@@@@@@@@@@@#=: ".to_string(), 30 | " :+@@@@@@@@*- ".to_string(), 31 | " .=%#=: ".to_string(), 32 | " ".to_string(), 33 | ]); 34 | 35 | ClassAscii { hunter } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/dclim/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dclim" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Command line tool for managing and syncing the remote Destiny 2 API manifest database." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | serde = "1.0.147" 19 | serde_json = "1.0.87" 20 | serde_derive = "1.0.147" 21 | reqwest = { version="0.11.12"} 22 | zip = "0.5.13" 23 | log = "0.4.17" 24 | env_logger = "0.9.3" 25 | 26 | dcli = { path = "../dcli/"} 27 | tell = { path = "../tell/"} 28 | -------------------------------------------------------------------------------- /src/dclim/README.md: -------------------------------------------------------------------------------- 1 | # dclim 2 | 3 | Command line tool for retrieving and managing the Destiny 2 manifest database. 4 | 5 | When running the utility will check whether a more current version of the Destiny 2 API manifest database is available (comparing it to the last version which has been downloaded). If a new version is found, it will download the database, un-compress it, and save it to the directory specified when calling the utility. It will also save a file containing metadata about the current version, which is used for future checks for updates. 6 | 7 | The manifest is stored in the system specific local app data directory with the file name: 8 | manifest.sqlite3, along with meta-data with information about the downloaded 9 | version. This is used to to determine whether the remote version has been updated. 10 | 11 | The utility expects that the downloaded manifest will not be moved from the directory it is downloaded to, and uses that information to determine whether a new version is available. If the manifest is moved, the utility will re-download the manifest on next check. 12 | 13 | The utility uses the download url for the manifest to check for a new version. While it displays the version number, that is not used to detect whether a new version is available. 14 | 15 | The manifest is a [Sqlite 3](https://www.sqlite.org/index.html) database. 16 | 17 | ## USAGE 18 | 19 | ``` 20 | USAGE: 21 | dclim [FLAGS] [OPTIONS] 22 | 23 | FLAGS: 24 | -K, --check 25 | Check whether a new manifest version is available, but do not download 26 | 27 | -F, --force 28 | Force a download of manifest regardless of whether it has been updated 29 | 30 | -h, --help 31 | Prints help information 32 | 33 | -V, --version 34 | Prints version information 35 | 36 | -v, --verbose 37 | Print out additional information 38 | 39 | OPTIONS: 40 | -D, --data-dir 41 | Directory where manifest will be stored. (optional) 42 | 43 | By default data will be loaded from and stored in the appropriate system local storage directory. Manifest 44 | will be stored in a sqlite3 database file named manifest.sqlite3 45 | -O, --output-format 46 | Format for command output 47 | 48 | Valid values are default (Default) and tsv. 49 | 50 | tsv outputs in a tab (\t) separated format of name / value pairs with lines ending in a new line character 51 | (\n). [default: default] 52 | ``` 53 | 54 | ### Examples 55 | 56 | #### Check for an updated manifest and store in default location: 57 | 58 | ``` 59 | $ dclim 60 | ``` 61 | 62 | which outputs: 63 | 64 | ``` 65 | Remote Manifest version 90085.20.12.12.2003-4 66 | Remote Manifest url https://www.bungie.net/common/destiny2_content/sqlite/en/world_sql_content_4538153d085eb7c87e59c58aefc70fb1.content 67 | Local Manifest version 90085.20.12.12.2003-4 68 | Local Manifest url https://www.bungie.net/common/destiny2_content/sqlite/en/world_sql_content_4538153d085eb7c87e59c58aefc70fb1.content 69 | Downloading manifest. This may take a bit of time. 70 | Manifest info saved. 71 | /home/mesh/tmp/tmp2/manifest.sqlite3 72 | ``` 73 | 74 | #### Download remote manifest and store in _~/manifest/_ directory regardless of whether remote is updated. 75 | 76 | ``` 77 | $ dclim --data-dir ~/manifest/ --force 78 | ``` 79 | 80 | #### Check status of remote manifest, but do not download. 81 | 82 | ``` 83 | $ dclim --check 84 | ``` 85 | 86 | which outputs: 87 | 88 | ``` 89 | https://www.bungie.net/Platform/Destiny2/Manifest/ 90 | Remote Manifest version 89360.20.11.18.2249-6 91 | Remote Manifest url https://www.bungie.net/common/destiny2_content/sqlite/en/world_sql_content_df27bd2a2e07a18c6f4b53c68449afd4.content 92 | Local Manifest version 89031.20.11.10.1952-1 93 | Local Manifest url https://www.bungie.net/common/destiny2_content/sqlite/en/world_sql_content_43b136a4cf20d3fe266da21319600a31.content 94 | Updated manifest available 89360.20.11.18.2249-6 95 | ``` 96 | 97 | #### Check for an updated manifest print output in a tab separated format (tsv) 98 | 99 | ``` 100 | $ dclim --output-format tsv 101 | ``` 102 | 103 | outputs: 104 | 105 | ``` 106 | local_path /Users/mesh/manifest/manifest.sqlite3 107 | updated true 108 | version 89360.20.11.18.2249-6 109 | ``` 110 | 111 | This shows that the local path for the manifest, and indicates that it was just updated. 112 | 113 | ## Questions, Feature Requests, Feedback 114 | 115 | If you have any questions, feature requests, need help, are running into issues, or just want to chat, join the [dcli Discord server](https://discord.gg/2Y8bV2Mq3p). 116 | 117 | You can also log bugs and features requests on the [issues page](https://github.com/mikechambers/dcli/issues). 118 | 119 | ### Environment Variables 120 | 121 | #### DCLI_FIX_DATA 122 | 123 | If the `DCLI_FIX_DATA` environment variable is set to `TRUE` then when corrupt or missing data is returned from the Bungie API, and there is not a valid local version, DCLI will attempt to retrieve updated, non-corrupt data from Bungie. (This sometimes happens if a lot of people leave a game, and no player names will be returned from the server). 124 | 125 | Setting this to true can significantly slow down sync time, especially the initial sync, and in general, is meant to be used when using DCLI to create datastores for larger applications. 126 | 127 | #### RUST_LOG 128 | 129 | All dcli apps have support for log output via the [env_logger](https://docs.rs/env_logger/0.9.3/env_logger/) library. This is mostly used for development, but may be helpful when trying to debug any issues. 130 | 131 | ## Compiling 132 | 133 | This utility is written and compiled in [Rust](https://www.rust-lang.org/). 134 | 135 | When compiling you must have an environment variable named `DESTINY_API_KEY` which contains your [Bungie API key](https://www.bungie.net/en/Application). 136 | 137 | To compile, switch to the `src/` directory and run: 138 | 139 | ``` 140 | $ cargo build --release 141 | ``` 142 | 143 | which will place the compiled tools in _src/target/release_ 144 | -------------------------------------------------------------------------------- /src/dclim/src/manifest_info.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use dcli::error::Error; 24 | use dcli::response::manifest::ManifestData; 25 | use serde_derive::{Deserialize, Serialize}; 26 | 27 | #[derive(Serialize, Deserialize, Debug)] 28 | pub struct ManifestInfo { 29 | pub version: String, 30 | pub url: String, 31 | } 32 | 33 | impl ManifestInfo { 34 | pub fn from_manifest(manifest: &ManifestData) -> ManifestInfo { 35 | ManifestInfo { 36 | version: String::from(&manifest.version), 37 | url: String::from(&manifest.mobile_world_content_paths.en), 38 | } 39 | } 40 | 41 | pub fn from_json(json: &str) -> Result { 42 | let m: ManifestInfo = serde_json::from_str(json)?; 43 | 44 | Ok(m) 45 | } 46 | 47 | pub fn to_json(&self) -> Result { 48 | //todo: do wes need to catch errors here? Would this ever fail? 49 | let out = serde_json::to_string(self)?; 50 | 51 | Ok(out) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/dclistat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dclistat" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Command line tool for querying specific Destiny 2 pvp stats." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | chrono = "0.4.23" 19 | num-format = "0.4.0" 20 | log = "0.4.17" 21 | env_logger = "0.9.3" 22 | 23 | dcli = { path = "../dcli/"} 24 | tell = {path = "../tell/"} 25 | -------------------------------------------------------------------------------- /src/dclisync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dclisync" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Command line tool for downloading and syncing Destiny 2 Crucible activity history." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | log = "0.4.17" 19 | env_logger = "0.9.3" 20 | 21 | dcli = { path = "../dcli/"} 22 | tell = { path = "../tell/"} 23 | 24 | [target.'cfg(not(windows))'.dependencies] 25 | signal-hook = "0.3.14" 26 | 27 | [target.'cfg(windows)'.dependencies] 28 | ctrlc = "3.2.2" 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/dclisync/service/dclisync.service: -------------------------------------------------------------------------------- 1 | #It does not require admin rights, but must be run in a user account 2 | #which has any required environment variables set (such as DESTINY_API_KEY 3 | #and which contains the dclisync executable in its path (you can also provide). 4 | #The user that it runs under will also impact where the database is stored. 5 | #make sure to replace /home/mesh below with appropriate path 6 | 7 | [Unit] 8 | Description=dclisync service 9 | After=multi-user.target 10 | 11 | [Service] 12 | Type=simple 13 | Restart=always 14 | User=mesh 15 | Group=mesh 16 | ExecStart=/usr/bin/bash -lc "dclisync --sync --daemon --interval 60" 17 | TimeoutStopSec=60 18 | [Install] 19 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /src/dclitime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dclitime" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Command line tool for retrieving Destiny 2 related date / time stamps" 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | structopt = "0.3.26" 17 | tokio = { version="1.21.2", features=["full"] } 18 | chrono = "0.4.23" 19 | log = "0.4.17" 20 | env_logger = "0.9.3" 21 | 22 | dcli = { path = "../dcli/" } 23 | tell = { path = "../tell/" } -------------------------------------------------------------------------------- /src/dclitime/README.md: -------------------------------------------------------------------------------- 1 | # dclitime 2 | 3 | Command line tool for retrieving date / time stamps for Destiny 2 weekly event moments. 4 | 5 | ## USAGE 6 | 7 | ``` 8 | USAGE: 9 | dclitime [FLAGS] [OPTIONS] 10 | 11 | FLAGS: 12 | -h, --help 13 | Prints help information 14 | 15 | -V, --version 16 | Prints version information 17 | 18 | -v, --verbose 19 | Print out additional information 20 | 21 | 22 | OPTIONS: 23 | -T, --moment 24 | The weekly Destiny 2 moment to retrieve the date / time stamp for 25 | 26 | Valid values are now, current_weekly (previous Tuesday weekly reset), next_weekly (upcoming Tuesday weekly 27 | reset), current_daily, next_daily, current_xur (previous Friday Xur reset), next_xur (upcoming Friday Xur 28 | reset), current_trials (previous Friday Trials reset), next_trials (upcoming Friday Trials reset) [default: 29 | now] 30 | -o, --output-format 31 | Format for command output 32 | 33 | Valid values are default (Default) and tsv. 34 | 35 | tsv outputs in a tab (\t) separated format of name / value pairs with lines ending in a new line character 36 | (\n). [default: default] 37 | -f, --time-format 38 | Date / time format to output moment 39 | 40 | Valid values are rfc3339 (default), rfc2822 and unix (unix timestamp, number of non-leap seconds since 41 | January 1, 1970 0:00:00 UTC). [default: rfc3339] 42 | ``` 43 | 44 | | ARGUMENT | OPTIONS | 45 | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 46 | | --moment | daily (last daily reset), weekend (last weekend reset on Friday), weekly (last weekly reset on Tuesday), day (last day), week (last week), month (last month), all_time, custom, launch, curse_of_osiris, warmind, season_of_the_outlaw, season_of_the_forge, season_of_the_drifter, season_of_opulence, season_of_the_undying, season_of_dawn, season_of_the_worthy, season_of_arrivals, season_of_the_hunt, season_of_the_chosen, season_of_the_splicer, season_of_the_lost, season_of_the_risen, witch_queen, season_of_the_haunted, season_of_plunder, season_of_the_seraph, lightfall, season_of_defiance, season_of_the_deep, season_of_the_witch, season_of_the_wish | 47 | | --time-format | rfc3339 (default), rfc2822, unix | 48 | 49 | | FORMATS | DESCRIPTION | 50 | | ------- | ----------------------------------------------------------------------------------------------------------------------- | 51 | | rfc3339 | [RFC3339](https://tools.ietf.org/html/rfc3339) standard date / time format: Example: _2020-12-07T02:59:59.187080+00:00_ | 52 | | rfc2822 | [RFC2822](https://tools.ietf.org/html/rfc2822) standard date / time format : Example: _Mon, 07 Dec 2020 03:00:30 +0000_ | 53 | | unix | Unix timestamp which is the number of non-leap seconds since January 1, 1970 0:00:00 UTC. Example: _1607446800_ | 54 | 55 | ### Examples 56 | 57 | #### Get date / time for the weekly Tuesday reset for the current week: 58 | 59 | ``` 60 | $ dclitime --moment weekly 61 | ``` 62 | 63 | #### Get date / time for the upcoming Xur reset on Friday in rfc2822 format: 64 | 65 | ``` 66 | $ dclitime --moment next_weekend --format rfc2822 67 | ``` 68 | 69 | #### Get date / time for next week's weekly reset on Tuesday and output in tab separated value format: 70 | 71 | ``` 72 | $ dclitime --moment next_weekly --output-format tsv 73 | ``` 74 | 75 | which outputs: 76 | 77 | ``` 78 | date_time 2020-12-08T17:00:00.774187+00:00 79 | format RFC 3339 80 | moment Next Weekly Reset 81 | ``` 82 | 83 | ## Questions, Feature Requests, Feedback 84 | 85 | If you have any questions, feature requests, need help, are running into issues, or just want to chat, join the [dcli Discord server](https://discord.gg/2Y8bV2Mq3p). 86 | 87 | You can also log bugs and features requests on the [issues page](https://github.com/mikechambers/dcli/issues). 88 | 89 | ### Environment Variables 90 | 91 | #### DCLI_FIX_DATA 92 | 93 | If the `DCLI_FIX_DATA` environment variable is set to `TRUE` then when corrupt or missing data is returned from the Bungie API, and there is not a valid local version, DCLI will attempt to retrieve updated, non-corrupt data from Bungie. (This sometimes happens if a lot of people leave a game, and no player names will be returned from the server). 94 | 95 | Setting this to true can significantly slow down sync time, especially the initial sync, and in general, is meant to be used when using DCLI to create datastores for larger applications. 96 | 97 | #### RUST_LOG 98 | 99 | All dcli apps have support for log output via the [env_logger](https://docs.rs/env_logger/0.9.3/env_logger/) library. This is mostly used for development, but may be helpful when trying to debug any issues. 100 | 101 | ## Compiling 102 | 103 | This utility is written and compiled in [Rust](https://www.rust-lang.org/). 104 | 105 | When compiling you must have an environment variable named `DESTINY_API_KEY` which contains your [Bungie API key](https://www.bungie.net/en/Application). 106 | 107 | To compile, switch to the `src/` directory and run: 108 | 109 | ``` 110 | $ cargo build --release 111 | ``` 112 | 113 | which will place the compiled tools in _src/target/release_ 114 | -------------------------------------------------------------------------------- /src/dclitime/src/datetimeformat.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | use std::fmt; 24 | use std::str::FromStr; 25 | 26 | #[derive(Eq, PartialEq, Debug)] 27 | pub enum DateTimeFormat { 28 | RFC2822, 29 | RFC3339, 30 | Unix, 31 | } 32 | 33 | impl FromStr for DateTimeFormat { 34 | type Err = &'static str; 35 | 36 | fn from_str(s: &str) -> Result { 37 | //wrap in String so we can convert to lower case 38 | let s = String::from(s).to_lowercase(); 39 | 40 | //get a slice to get a &str for the match 41 | match &s[..] { 42 | "rfc2822" => Ok(DateTimeFormat::RFC2822), 43 | "rfc3339" => Ok(DateTimeFormat::RFC3339), 44 | "unix" => Ok(DateTimeFormat::Unix), 45 | 46 | _ => Err("Unknown DateTimeFormat type"), 47 | } 48 | } 49 | } 50 | 51 | impl fmt::Display for DateTimeFormat { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | let out = match self { 54 | DateTimeFormat::RFC2822 => " RFC 2822", 55 | DateTimeFormat::RFC3339 => "RFC 3339", 56 | DateTimeFormat::Unix => "Unix Timestamp", 57 | }; 58 | 59 | write!(f, "{}", out) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/dclitime/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | mod datetimeformat; 24 | 25 | use datetimeformat::DateTimeFormat; 26 | use dcli::enums::moment::Moment; 27 | use dcli::output::Output; 28 | use dcli::utils::build_tsv; 29 | use structopt::StructOpt; 30 | use tell::{Tell, TellLevel}; 31 | 32 | #[derive(StructOpt, Debug)] 33 | #[structopt(verbatim_doc_comment)] 34 | /// Command line tool for retrieving date / time stamps for Destiny 2 weekly event 35 | /// moments 36 | /// 37 | /// Created by Mike Chambers. 38 | /// https://www.mikechambers.com 39 | /// 40 | /// Get support, request features or just chat on the dcli Discord server: 41 | /// https://discord.gg/2Y8bV2Mq3p 42 | /// 43 | /// Get the latest version, download the source and log issues at: 44 | /// https://github.com/mikechambers/dcli 45 | /// 46 | /// Released under an MIT License. 47 | struct Opt { 48 | /// The weekly Destiny 2 moment to retrieve the date / time stamp for 49 | /// 50 | /// Valid values are now, current_weekly (previous Tuesday weekly reset), 51 | /// next_weekly (upcoming Tuesday weekly reset), current_daily, next_daily, 52 | /// current_xur (previous Friday Xur reset), next_xur (upcoming Friday Xur reset), 53 | /// current_trials (previous Friday Trials reset), next_trials (upcoming Friday Trials reset) 54 | #[structopt(short = "T", long = "moment", default_value = "now")] 55 | moment: Moment, 56 | 57 | /// Date / time format to output moment 58 | /// 59 | /// Valid values are rfc3339 (default), rfc2822 and unix (unix timestamp, 60 | /// number of non-leap seconds since January 1, 1970 0:00:00 UTC). 61 | #[structopt(short = "f", long = "time-format", default_value = "rfc3339")] 62 | time_format: DateTimeFormat, 63 | 64 | /// Print out additional information 65 | #[structopt(short = "v", long = "verbose")] 66 | verbose: bool, 67 | 68 | /// Format for command output 69 | /// 70 | /// Valid values are default (Default) and tsv. 71 | /// 72 | /// tsv outputs in a tab (\t) separated format of name / value pairs with lines 73 | /// ending in a new line character (\n). 74 | #[structopt( 75 | short = "O", 76 | long = "output-format", 77 | default_value = "default" 78 | )] 79 | output: Output, 80 | } 81 | 82 | #[tokio::main] 83 | async fn main() { 84 | let opt = Opt::from_args(); 85 | 86 | let level = if opt.verbose { 87 | TellLevel::Verbose 88 | } else { 89 | TellLevel::Progress 90 | }; 91 | 92 | Tell::init(level); 93 | 94 | log::info!("{:#?}", opt.verbose); 95 | tell::verbose!("{:#?}", opt.verbose); 96 | 97 | let dt = opt.moment.get_date_time(); 98 | let date_time_str = match opt.time_format { 99 | DateTimeFormat::RFC3339 => dt.to_rfc3339(), 100 | DateTimeFormat::RFC2822 => dt.to_rfc2822(), 101 | DateTimeFormat::Unix => dt.timestamp().to_string(), 102 | }; 103 | 104 | match opt.output { 105 | Output::Default => { 106 | tell::update!("{}", date_time_str); 107 | } 108 | Output::Tsv => { 109 | let mut name_values: Vec<(&str, String)> = Vec::new(); 110 | name_values.push(("date_time", date_time_str)); 111 | name_values.push(("format", format!("{}", opt.time_format))); 112 | name_values.push(("moment", format!("{}", opt.moment))); 113 | 114 | tell::update!("{}", build_tsv(name_values)); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.65.0" -------------------------------------------------------------------------------- /src/rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | #group_imports = "StdExternalCrate" 3 | reorder_modules = true 4 | max_width = 80 5 | -------------------------------------------------------------------------------- /src/tell/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tell" 3 | #version 4 | version = "0.99.9" 5 | authors = ["Mike Chambers "] 6 | edition = "2018" 7 | description = "Simple library for handling different levels of user output for command line apps." 8 | homepage = "https://www.mikechambers.com" 9 | repository = "https://github.com/mikechambers/dcli" 10 | license = "MIT" 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | lazy_static = "1.4.0" 15 | -------------------------------------------------------------------------------- /src/tell/README.md: -------------------------------------------------------------------------------- 1 | # tell 2 | 3 | Simple library for handling different levels of user output for command line apps. 4 | -------------------------------------------------------------------------------- /src/tell/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | mod macros; 24 | 25 | use lazy_static::lazy_static; 26 | use std::cmp; 27 | use std::fmt; 28 | use std::sync::RwLock; 29 | 30 | #[derive(Eq, PartialEq, Clone, Copy)] 31 | pub enum TellLevel { 32 | Silent = 0, 33 | Error, 34 | Update, //general update on status 35 | Progress, //progress update 36 | Verbose, 37 | } 38 | 39 | impl fmt::Display for TellLevel { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | let out = match self { 42 | TellLevel::Silent => "Silent", 43 | TellLevel::Error => "Error", 44 | TellLevel::Update => "Update", 45 | TellLevel::Progress => "Progress", 46 | TellLevel::Verbose => "Verbose", 47 | }; 48 | 49 | write!(f, "{}", out) 50 | } 51 | } 52 | 53 | impl PartialOrd for TellLevel { 54 | fn partial_cmp(&self, other: &Self) -> Option { 55 | let s = *self as usize; 56 | let o = *other as usize; 57 | s.partial_cmp(&o) 58 | } 59 | } 60 | 61 | lazy_static! { 62 | 63 | #[allow(non_upper_case_globals)] 64 | static ref TL: RwLock = RwLock::new(TellLevel::Update); 65 | } 66 | 67 | pub struct Tell; 68 | 69 | impl Tell { 70 | pub fn is_active(level: TellLevel) -> bool { 71 | let t = TL.write().unwrap(); 72 | 73 | level <= *t 74 | } 75 | 76 | pub fn set_level(level: TellLevel) { 77 | let mut t = TL.write().unwrap(); 78 | *t = level; 79 | } 80 | 81 | pub fn init(level: TellLevel) { 82 | Tell::set_level(level); 83 | } 84 | 85 | pub fn print(level: TellLevel, msg: &str) { 86 | if !Tell::is_active(level) || level == TellLevel::Silent { 87 | return; 88 | } 89 | 90 | match level { 91 | TellLevel::Error => { 92 | eprintln!("{}", msg); 93 | } 94 | _ => { 95 | println!("{}", msg); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/tell/src/macros.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mike Chambers 3 | * https://github.com/mikechambers/dcli 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is furnished to do 10 | * so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #[macro_export] 24 | macro_rules! t { 25 | 26 | //($lvl:expr, $($arg:tt)+) => (log!(target: __log_module_path!(), $lvl, $($arg)+)); 27 | ($lvl:expr, $($arg:tt)+) => { 28 | tell::Tell::print($lvl, &(format_args!($($arg)+)).to_string()); 29 | }; 30 | } 31 | 32 | //TODO: this works. copy to other functions 33 | //move files into lib 34 | #[macro_export] 35 | macro_rules! update { 36 | () => (tell::t!(tell::TellLevel::Update, "")); 37 | ($($arg:tt)+) => (tell::t!(tell::TellLevel::Update, $($arg)+)); 38 | } 39 | 40 | #[macro_export] 41 | macro_rules! progress { 42 | () => (tell::t!(tell::TellLevel::Progress, "")); 43 | ($($arg:tt)+) => (tell::t!(tell::TellLevel::Progress, $($arg)+)); 44 | } 45 | 46 | #[macro_export] 47 | macro_rules! error { 48 | () => (tell::t!(tell::TellLevel::Error, "")); 49 | ($($arg:tt)+) => (tell::t!(tell::TellLevel::Error, $($arg)+)); 50 | } 51 | 52 | #[macro_export] 53 | macro_rules! verbose { 54 | () => (tell::t!(tell::TellLevel::Verbose, "")); 55 | ($($arg:tt)+) => (tell::t!(tell::TellLevel::Verbose, $($arg)+)); 56 | } 57 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # dcli tests 2 | 3 | This folder contains scripts for simple testing of the compiled apps. 4 | 5 | * **runapps** Runs all dcli apps on via a bash shell 6 | * **runapps.bat** Windows bat file to run all apps 7 | 8 | Both scripts expect that the apps can be found in the system / user PATH. -------------------------------------------------------------------------------- /tests/runapps: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Simple script that just runs all of the dcli apps 4 | # 5 | # Created by Mike Chambers 6 | # https://www.mikechambers.com 7 | # 8 | # Released under an MIT License 9 | # 10 | # More info at: 11 | # https://github.com/mikechambers/dcli/ 12 | 13 | 14 | echo "------------- RUNNING dclis--------------" 15 | dclis --name mesh --platform xbox 16 | 17 | echo "------------- RUNNING dclim--------------" 18 | dclim 19 | 20 | echo "------------- RUNNING dclic--------------" 21 | dclic --name mesh#3230 22 | 23 | echo "------------- RUNNING dclims--------------" 24 | dclims --hash 3260604718 25 | 26 | echo "------------- RUNNING dclia--------------" 27 | dclia --name mesh#3230 28 | 29 | echo "------------- RUNNING dclitime--------------" 30 | dclitime 31 | 32 | echo "------------- RUNNING dcliah --------------" 33 | dcliah --name mesh#3230 34 | 35 | echo "------------- RUNNING dcliad --------------" 36 | dcliad --name mesh#3230 37 | -------------------------------------------------------------------------------- /tests/runapps.bat: -------------------------------------------------------------------------------- 1 | 2 | :: Simple script that just runs all of the dcli apps on Windows 3 | :: 4 | :: Created by Mike Chambers 5 | :: https://www.mikechambers.com 6 | :: 7 | :: Released under an MIT License 8 | :: 9 | :: More info at: 10 | :: https://github.com/mikechambers/dcli/ 11 | 12 | @echo off 13 | 14 | echo ------------- RUNNING dclis.exe-------------- 15 | 16 | call dclis.exe --name mesh --platform xbox 17 | 18 | echo ------------- RUNNING dclim.exe-------------- 19 | 20 | call dclim.exe 21 | 22 | echo ------------- RUNNING dclic.exe-------------- 23 | 24 | call dclic.exe --name mesh#3230 25 | 26 | echo ------------- RUNNING dclims.exe-------------- 27 | 28 | call dclims.exe --hash 3260604718 29 | 30 | echo ------------- RUNNING dclia.exe-------------- 31 | 32 | call dclia.exe --name mesh#3230 33 | 34 | echo ------------- RUNNING dcliah.exe -------------- 35 | 36 | dcliah.exe --name mesh#3230 37 | 38 | echo ------------- RUNNING dcliad.exe -------------- 39 | 40 | dcliad.exe --name mesh#3230 41 | 42 | echo ------------- RUNNING dclitime.exe --------------- 43 | 44 | call dclitime.exe 45 | 46 | 47 | -------------------------------------------------------------------------------- /wiki/running-on-osx/dialog-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/wiki/running-on-osx/dialog-1.png -------------------------------------------------------------------------------- /wiki/running-on-osx/dialog-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/wiki/running-on-osx/dialog-2.png -------------------------------------------------------------------------------- /wiki/running-on-osx/dialog-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikechambers/dcli/15a5c789c5c91f9bf3f5ffacd13ea8cfee8d6893/wiki/running-on-osx/dialog-3.png --------------------------------------------------------------------------------