diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..3729ff0c
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/.github/images/dashboard/alarm-edit.png b/.github/images/dashboard/alarm-edit.png
new file mode 100644
index 00000000..72e5295f
Binary files /dev/null and b/.github/images/dashboard/alarm-edit.png differ
diff --git a/.github/images/dashboard/alarms.png b/.github/images/dashboard/alarms.png
new file mode 100644
index 00000000..0aa06848
Binary files /dev/null and b/.github/images/dashboard/alarms.png differ
diff --git a/.github/images/dashboard/config-edit.png b/.github/images/dashboard/config-edit.png
new file mode 100644
index 00000000..1f5251d1
Binary files /dev/null and b/.github/images/dashboard/config-edit.png differ
diff --git a/.github/images/dashboard/configs.png b/.github/images/dashboard/configs.png
new file mode 100644
index 00000000..4fedd165
Binary files /dev/null and b/.github/images/dashboard/configs.png differ
diff --git a/.github/images/dashboard/dashboard.png b/.github/images/dashboard/dashboard.png
new file mode 100644
index 00000000..6748f4c4
Binary files /dev/null and b/.github/images/dashboard/dashboard.png differ
diff --git a/.github/images/dashboard/discord-edit.png b/.github/images/dashboard/discord-edit.png
new file mode 100644
index 00000000..84741354
Binary files /dev/null and b/.github/images/dashboard/discord-edit.png differ
diff --git a/.github/images/dashboard/discords.png b/.github/images/dashboard/discords.png
new file mode 100644
index 00000000..9eb080f5
Binary files /dev/null and b/.github/images/dashboard/discords.png differ
diff --git a/.github/images/dashboard/embed-edit.png b/.github/images/dashboard/embed-edit.png
new file mode 100644
index 00000000..5a3ddb16
Binary files /dev/null and b/.github/images/dashboard/embed-edit.png differ
diff --git a/.github/images/dashboard/embed-new.png b/.github/images/dashboard/embed-new.png
new file mode 100644
index 00000000..ca1e92d4
Binary files /dev/null and b/.github/images/dashboard/embed-new.png differ
diff --git a/.github/images/dashboard/embeds.png b/.github/images/dashboard/embeds.png
new file mode 100644
index 00000000..c8023ddf
Binary files /dev/null and b/.github/images/dashboard/embeds.png differ
diff --git a/.github/images/dashboard/filter-edit.png b/.github/images/dashboard/filter-edit.png
new file mode 100644
index 00000000..6955d806
Binary files /dev/null and b/.github/images/dashboard/filter-edit.png differ
diff --git a/.github/images/dashboard/filters.png b/.github/images/dashboard/filters.png
new file mode 100644
index 00000000..0abef7f2
Binary files /dev/null and b/.github/images/dashboard/filters.png differ
diff --git a/.github/images/dashboard/geofence-edit.png b/.github/images/dashboard/geofence-edit.png
new file mode 100644
index 00000000..ce9a69a4
Binary files /dev/null and b/.github/images/dashboard/geofence-edit.png differ
diff --git a/.github/images/dashboard/geofence-export.png b/.github/images/dashboard/geofence-export.png
new file mode 100644
index 00000000..eaa31641
Binary files /dev/null and b/.github/images/dashboard/geofence-export.png differ
diff --git a/.github/images/dashboard/geofences.png b/.github/images/dashboard/geofences.png
new file mode 100644
index 00000000..43ee0d0b
Binary files /dev/null and b/.github/images/dashboard/geofences.png differ
diff --git a/.github/images/dashboard/role-edit.png b/.github/images/dashboard/role-edit.png
new file mode 100644
index 00000000..848ed145
Binary files /dev/null and b/.github/images/dashboard/role-edit.png differ
diff --git a/.github/images/dashboard/roles.png b/.github/images/dashboard/roles.png
new file mode 100644
index 00000000..737cc866
Binary files /dev/null and b/.github/images/dashboard/roles.png differ
diff --git a/.github/images/eggs.png b/.github/images/eggs.png
new file mode 100644
index 00000000..a23d255c
Binary files /dev/null and b/.github/images/eggs.png differ
diff --git a/.github/images/gyms.png b/.github/images/gyms.png
new file mode 100644
index 00000000..dff26b50
Binary files /dev/null and b/.github/images/gyms.png differ
diff --git a/.github/images/invasions.png b/.github/images/invasions.png
new file mode 100644
index 00000000..e476a7ba
Binary files /dev/null and b/.github/images/invasions.png differ
diff --git a/.github/images/lure.png b/.github/images/lure.png
new file mode 100644
index 00000000..6830dead
Binary files /dev/null and b/.github/images/lure.png differ
diff --git a/.github/images/lure_glacial.png b/.github/images/lure_glacial.png
new file mode 100644
index 00000000..eec44888
Binary files /dev/null and b/.github/images/lure_glacial.png differ
diff --git a/.github/images/lure_magnetic.png b/.github/images/lure_magnetic.png
new file mode 100644
index 00000000..2a642181
Binary files /dev/null and b/.github/images/lure_magnetic.png differ
diff --git a/.github/images/lure_mossy.png b/.github/images/lure_mossy.png
new file mode 100644
index 00000000..ca6238db
Binary files /dev/null and b/.github/images/lure_mossy.png differ
diff --git a/.github/images/lure_rainy.png b/.github/images/lure_rainy.png
new file mode 100644
index 00000000..cbb46055
Binary files /dev/null and b/.github/images/lure_rainy.png differ
diff --git a/.github/images/pkmn.png b/.github/images/pkmn.png
new file mode 100644
index 00000000..ab99f798
Binary files /dev/null and b/.github/images/pkmn.png differ
diff --git a/.github/images/pvp.png b/.github/images/pvp.png
new file mode 100644
index 00000000..8c316d70
Binary files /dev/null and b/.github/images/pvp.png differ
diff --git a/.github/images/quests.png b/.github/images/quests.png
new file mode 100644
index 00000000..153ad556
Binary files /dev/null and b/.github/images/quests.png differ
diff --git a/.github/images/raids.png b/.github/images/raids.png
new file mode 100644
index 00000000..a1c4184f
Binary files /dev/null and b/.github/images/raids.png differ
diff --git a/images/weather.png b/.github/images/weather.png
similarity index 100%
rename from images/weather.png
rename to .github/images/weather.png
diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnet.yml
similarity index 74%
rename from .github/workflows/dotnetcore.yml
rename to .github/workflows/dotnet.yml
index 04cd80f7..4e4cfb27 100644
--- a/.github/workflows/dotnetcore.yml
+++ b/.github/workflows/dotnet.yml
@@ -1,4 +1,4 @@
-name: .NET Core
+name: .NET 5.0
on:
push:
@@ -6,15 +6,15 @@ on:
jobs:
build:
- name: Test on .NET Core ${{ matrix.dotnet }} and ${{ matrix.os }}
+ name: Test on .NET ${{ matrix.dotnet }} and ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
- dotnet: [ '2.1.803' ]
+ dotnet: [ '5.0.404' ]
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- uses: actions/checkout@v2
- - name: Setup .NET Core
+ - name: Setup .NET 5.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ matrix.dotnet }}
diff --git a/.gitignore b/.gitignore
index 72d2ddbd..fb537182 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,333 +1,335 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-
-# User-specific files
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-[Cc]onfig.json
-
-# Visual Studio 2015/2017 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# Visual Studio 2017 auto generated files
-Generated\ Files/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUNIT
-*.VisualState.xml
-TestResult.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# Benchmark Results
-BenchmarkDotNet.Artifacts/
-
-# .NET Core
-project.lock.json
-project.fragment.lock.json
-artifacts/
-**/Properties/launchSettings.json
-
-# StyleCop
-StyleCopReport.xml
-
-# Files built by Visual Studio
-*_i.c
-*_p.c
-*_i.h
-*.ilk
-*.meta
-*.obj
-*.iobj
-*.pch
-*.pdb
-*.ipdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# Visual Studio Trace Files
-*.e2e
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# JustCode is a .NET coding add-in
-.JustCode
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# AxoCover is a Code Coverage Tool
-.axoCover/*
-!.axoCover/settings.json
-
-# Visual Studio code coverage results
-*.coverage
-*.coveragexml
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# Note: Comment the next line if you want to checkin your web deploy settings,
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
-# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/[Pp]ackages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-*.appx
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.jfm
-*.pfx
-*.publishsettings
-orleans.codegen.cs
-
-# Including strong name files can present a security risk
-# (https://github.com/github/gitignore/pull/2483#issue-259490424)
-#*.snk
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-ServiceFabricBackup/
-*.rptproj.bak
-
-# SQL Server files
-*.mdf
-*.ldf
-*.ndf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-*.rptproj.rsuser
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-node_modules/
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
-*.vbw
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# JetBrains Rider
-.idea/
-*.sln.iml
-
-# CodeRush
-.cr/
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
-
-# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
-
-# Tabs Studio
-*.tss
-
-# Telerik's JustMock configuration file
-*.jmconfig
-
-# BizTalk build output
-*.btp.cs
-*.btm.cs
-*.odx.cs
-*.xsd.cs
-
-# OpenCover UI analysis results
-OpenCover/
-
-# Azure Stream Analytics local run output
-ASALocalRun/
-
-# MSBuild Binary and Structured Log
-*.binlog
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
-
-.leu
\ No newline at end of file
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Cc]onfig.json
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+.leu
+
+.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
index f809e3f7..f46d2754 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,13 @@
-[](https://github.com/versx/WhMgr/actions)
+[](https://github.com/versx/WhMgr/actions)
[](https://whmgr.rtfd.io)
[](https://github.com/versx/WhMgr/releases/)
[](https://github.com/versx/WhMgr/graphs/contributors/)
[](https://discord.gg/zZ9h9Xa)
+
# Webhook Manager
-### PokeAlarm, PoracleJS, WDR, Novabot, etc alternative.
+**PokeAlarm, PoracleJS, WDR, Novabot, etc alternative.**
+
Works with the following backends:
- [RealDeviceMap](https://github.com/123FLO321/RealDeviceMap)
- [Chuck](https://github.com/WatWowMap/Chuck)
@@ -13,80 +15,88 @@ Works with the following backends:
## Description
-Sends Discord notifications based on pre-defined filters for Pokemon, raids, raid eggs, field research quests, Team Rocket invasions, gym team changes, and weather. Also supports Discord user's subscribing to Pokemon, raid, quest, Team Rocket invasion, and Pokestop lure notifications via direct messages.
+Developed in C#, runs on .NET 5.0 ASP.NET CoreCLR utilizing EntityFramework Core. Cross platform compatibility with Windows, macOS, and Linux operating systems.
+Sends Discord notifications based on pre-defined filters for Pokemon, raids, raid eggs, field research quests, Team Rocket invasions, Pokestop lures, gym team changes, and weather changes. It also supports Discord users subscribing to Pokemon, PvP, raid, quest, gym, Team Rocket invasion, and Pokestop lure notifications via direct messages.
-## Features
+### Features
- Supports multiple Discord servers.
-- Discord channel alarm reports for Pokemon, raids, eggs, quests, lures, invasions, gym team changes, and weather.
-- Per user custom Discord notifications for Pokemon, raids, quests, invasions, and lures.
-- User interface to configure Discord notifications with ease (as well as Discord commands). [WhMgr-UI](https://github.com/versx/WhMgr-UI)
-- Subscription notifications based on pre-defined distance.
-- Customizable alert messages with dynamic text replacement.
+- Discord channel alarm reports for Pokemon, raids, eggs, quests, lures, invasions, gym team changes, and weather changes.
+- Built-in Admin Dashboard to configure and manage configuration files.
+- Webhook and subscription queue system, all outgoing messages are queued and fired off in groups for efficiency.
+- If an outgoing message is rate limited, it is backlogged and awaited the rate limit time then requeued.
+- Per user custom Discord notifications for Pokemon, raids, quests, invasions, lures, and gyms.
+- User interface to configure custom Discord subscription notifications with ease. [WhMgr-UI](https://github.com/versx/WhMgr-UI)
+- Subscription notifications based on pre-defined distance and geofence areas.
+- Customizable alert messages with dynamic text replacement/substitution.
- Support for multiple cities/areas using geofences per server.
-- Daily shiny stats reporting.
-- Automatic quest message purge at midnight.
-- Support for Donors/Supporters only notifications.
-- Direct messages of Pokemon notifications based on city roles assigned.
-- Pokemon and Raid subscription notifications based on specific forms.
-- Custom prefix support as well as mentionable user support for commands.
+- Daily shiny and IV stats reporting.
+- Automatic quest message purge at midnight based on timezone.
+- Support for Subscriber only custom notifications.
+- Pokemon, PvP, and Raid subscription notifications based on specific forms or costumes.
+- Custom prefix support as well as mentionable bot user string for commands.
- Raid subscription notifications for specific gyms.
- Twilio text message alerts for ultra rare Pokemon.
- Custom image support for Discord alarm reports.
- Custom icon style selection for Discord user notifications.
- External emoji server support.
-- Custom static map format support.
-- Support for language translation.
+- Custom static map format support, including pokestop and gym marker placements.
+- Support for language translation per instance (per server planned).
- Multi threaded, low processing consumption.
-- [I.C.O.N.S.](https://github.com/Mygod/pokemon-icon-postprocessor) standard image support.
+- [UIcons](https://github.com/uicons/uicons) standard image support.
- Lots more...
-## [Documentation](https://whmgr.rtfd.io/)
+## [Documentation](https://whmgr.rtfd.io/en/v5-rewrite/)
-### [Getting Started Guide](https://whmgr.readthedocs.io/en/latest/user-guide/getting-started)
+### [Getting Started Guide](https://whmgr.readthedocs.io/en/v5-rewrite/install/getting-started)
## Previews
*All examples are completely customizable using Dynamic Text Replacement/Substitution*
-Discord Pokemon Notifications:
-
-Discord Pokemon PVP Notifications:
-
+__Pokemon Notifications__
+
+
+__Pokemon PVP Notifications__
+
+
+__Raid Boss Notifications__
+
-Discord Raid Notifications:
-
+__Raid Egg Notifications__
+
-Discord Raid Egg Notifications:
-
+__Quest Notifications__
+
-Discord Quest Notifications:
-
+__Lure Notifications__
+
-Discord Lure Notifications:
-
+__Lure (Glacial) Notifications__
+ Notifications")
-Discord Lure (Glacial) Notifications:
- Notifications")
+__Lure (Mossy) Notifications__
+ Notifications")
-Discord Lure (Mossy) Notifications:
- Notifications")
+__Lure (Magnetic) Notifications__
+ Notifications")
-Discord Lure (Magnetic) Notifications:
- Notifications")
+__Lure (Rainy) Notifications__
+ Notifications")
-Discord Gym Team Takeover Notifications:
-
+__Gym Team Takeover Notifications__
+
-Discord Team Rocket Invasion Notifications:
-
+__Team Rocket Invasion Notifications__
+
-Discord Weather Notifications:
-
+__Weather Notifications__
+
## Credits
[versx](https://github.com/versx) - Developer
[PokeAlarm](https://github.com/PokeAlarm/PokeAlarm) - Dynamic Text Substitution idea
[WDR](https://github.com/PartTimeJS/WDR) - masterfile.json file
+[Contributors](https://github.com/versx/WhMgr/contributors)
diff --git a/WhMgr.sln b/WhMgr.sln
index 5bfe50b0..68306a78 100644
--- a/WhMgr.sln
+++ b/WhMgr.sln
@@ -1,31 +1,31 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30114.105
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhMgr", "src\WhMgr.csproj", "{82454CE5-D056-468E-93AA-C951567989A2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhMgr.Test", "test\WhMgr.Test.csproj", "{2E9E0AE2-101B-40AA-85F3-A144C057BE60}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {82454CE5-D056-468E-93AA-C951567989A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {82454CE5-D056-468E-93AA-C951567989A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {82454CE5-D056-468E-93AA-C951567989A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {82454CE5-D056-468E-93AA-C951567989A2}.Release|Any CPU.Build.0 = Release|Any CPU
- {2E9E0AE2-101B-40AA-85F3-A144C057BE60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2E9E0AE2-101B-40AA-85F3-A144C057BE60}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2E9E0AE2-101B-40AA-85F3-A144C057BE60}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2E9E0AE2-101B-40AA-85F3-A144C057BE60}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {8DB5EDA2-DFED-471B-A84C-133865C086D4}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.32112.339
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhMgr", "src\WhMgr.csproj", "{6B59CEAE-3BFA-4E31-B86E-0AA276C4796A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhMgr.Test", "test\WhMgr.Test.csproj", "{2BBAB7C9-3F59-4E88-9477-481C6466C4F5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6B59CEAE-3BFA-4E31-B86E-0AA276C4796A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B59CEAE-3BFA-4E31-B86E-0AA276C4796A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B59CEAE-3BFA-4E31-B86E-0AA276C4796A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B59CEAE-3BFA-4E31-B86E-0AA276C4796A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2BBAB7C9-3F59-4E88-9477-481C6466C4F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2BBAB7C9-3F59-4E88-9477-481C6466C4F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2BBAB7C9-3F59-4E88-9477-481C6466C4F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2BBAB7C9-3F59-4E88-9477-481C6466C4F5}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B6D16BB5-6A62-4232-B680-96B3364EA1B0}
+ EndGlobalSection
+EndGlobal
diff --git a/config.example.json b/config.example.json
deleted file mode 100644
index db889a3f..00000000
--- a/config.example.json
+++ /dev/null
@@ -1,73 +0,0 @@
-{
- "host": "*",
- "port": 8008,
- "locale": "en",
- "shortUrlApiUrl": null,
- "stripeApiKey": "",
- "maxPokemonId": 721,
- "servers": {
- "000000000000000001": "discord1.example.json",
- "000000000000000002": "discord2.example.json"
- },
- "database": {
- "main": {
- "host": "127.0.0.1",
- "port": 3306,
- "username": "root",
- "password": "password",
- "database": "brockdb"
- },
- "scanner": {
- "host": "127.0.0.1",
- "port": 3306,
- "username": "root",
- "password": "password",
- "database": "rdmdb"
- },
- "nests": {
- "host": "127.0.0.1",
- "port": 3306,
- "username": "root",
- "password": "password",
- "database": "manualdb"
- }
- },
- "eventPokemonIds": [],
- "eventMinimumIV": "90",
- "urls": {
- "staticMap": "http://tiles.example.com:8080",
- "scannerMap": "http://map.example.com/@/{0}/{1}/15"
- },
- "iconStyles": {
- "Default": "https://raw.githubusercontent.com/nileplumb/PkmnHomeIcons/ICONS/ICONS/",
- "Shuffle": "https://raw.githubusercontent.com/nileplumb/PkmnShuffleMap/master/ICONS_STANDARD/"
- },
- "staticMaps": {
- "pokemon": "pokemon.example",
- "raids": "raids.example",
- "gyms": "gyms.example",
- "quests": "quests.example",
- "invasions": "invasions.example",
- "lures": "lures.example",
- "weather": "weather.example",
- "nests": "nests.example"
- },
- "twilio": {
- "enabled": false,
- "accountSid": "",
- "authToken": "",
- "from": "",
- "userIds": [],
- "roleIds": [],
- "pokemonIds": [201, 480, 481, 482, 443, 444, 445, 633, 634, 635, 610, 611, 612],
- "minIV": 100
- },
- "gmapsKey": "",
- "nominatim": "",
- "nominatimSchema": "{DisplayName}",
- "despawnTimeMinimumMinutes": 5,
- "reloadSubscriptionChangesMinutes": 1,
- "checkForDuplicates": true,
- "debug": false,
- "logLevel": "Trace"
-}
diff --git a/docs/commands/subscriptions.md b/docs/commands/subscriptions.md
index 0809de79..47ba820a 100644
--- a/docs/commands/subscriptions.md
+++ b/docs/commands/subscriptions.md
@@ -11,6 +11,7 @@ Parameters in `[]` are optional parameters and default values will be used if no
**expire** / **expires** - Check stripe API when Donor/Supporter subscription expires.
**set-number** - Set a phone number to receive text message alerts for ultra rare Pokemon.
+
**set-distance** - Set minimum distance to Pokemon, raids, quests, invasions and gyms need to be within. (Measured in meters)
Usage: `set-distance ,`
@@ -244,6 +245,7 @@ Examples:
**import** - Import saved subscriptions file.
**export** - Export subscriptions config file.
+
### Icon Style
diff --git a/docs/user-guide/alarms.md b/docs/config/alarms.md
similarity index 74%
rename from docs/user-guide/alarms.md
rename to docs/config/alarms.md
index b73333a6..eac67706 100644
--- a/docs/user-guide/alarms.md
+++ b/docs/config/alarms.md
@@ -6,23 +6,25 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
**Notes:**
- Place your active alarms in your `alarms` folder
-- Discord webhook permissions are based on the EVERYONE role permission, if you plan to use an external emoji server you *MUST* ensure the everyone role has the "use external emojis" permission on the destination channel. Even if you have the channel locked to a donor type role, the everyone role still needs this permission enabled. Remember setting everyone role to allow external emoji's at server level but an explicit deny on a channel will prevent them from showing
+- Discord webhook permissions are based on the EVERYONE role permission, if you plan to use an external emoji server you *MUST* ensure the everyone role has the "use external emojis" permission on the destination channel. Even if you have the channel locked to a donor type role, the everyone role still needs this permission enabled. Remember setting everyone role to allow external emoji's at server level but an explicit deny on a channel will prevent them from showing.
## Example
-```js
+```json
{
- // Enable or disable Pokemon alarms globally
+ // Enable or disable Pokemon filters globally
"enablePokemon": true,
- // Enable or disable Raid alarms globally
+ // Enable or disable Raid filters globally
"enableRaids": true,
- // Enable or disable Quest alarms globally
+ // Enable or disable Quest filters globally
"enableQuests": true,
- // Enable or disable Pokestop alarms globally
+ // Enable or disable Pokestop filters globally
"enablePokestops": true,
- // Enable or disable Gym alarms globally
+ // Enable or disable Invasion filters globally
+ "enableInvasions": true,
+ // Enable or disable Gym filters globally
"enableGyms": true,
- // Enable or disable Weather alarms globally
+ // Enable or disable Weather filters globally
"enableWeather": true,
// List of alarms
"alarms":
@@ -30,12 +32,12 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City1-Rare",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"all.json",
// Mentionable string that supports DTS (!@ for user, @& for role)
- "description":" <@&1123454> L ",
+ "description": " <@&12331131> {{iv}} L{{lvl}} {{geofence}}",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
"geofences": ["geofence1.txt", "city1"],
// Discord webhook url address
@@ -44,8 +46,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// 100% IV alarm for City1
"name":"City1-100iv",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"100iv.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -56,8 +58,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City1-Raids",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"raids.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -68,8 +70,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City1-LegendaryRaids",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"legendary_raids.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -80,8 +82,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City1-ExRaids",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"ex_raids.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -92,8 +94,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City1-Quests",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "quests.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -104,8 +106,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City1-Lures",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "lures.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -116,8 +118,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City1-Invasions",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "invasions.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -128,8 +130,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City1-Gyms",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "gyms.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -140,8 +142,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City2-Rare",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"all.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -152,8 +154,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City2-100iv",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"100iv.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -164,8 +166,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City2-Raids",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"raids.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -176,8 +178,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City2-LegendaryRaids",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"legendary_raids.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -188,8 +190,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"City2-ExRaids",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"ex_raids.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -200,8 +202,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City2-Quests",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "quests.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -212,8 +214,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City2-Lures",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "lures.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -224,8 +226,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City2-Invasions",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "invasions.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -236,8 +238,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name": "City2-Gyms",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters": "gyms.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
@@ -248,8 +250,8 @@ There is no limit to the amount of alarms you can add under the `alarms` propert
{
// Alarm name
"name":"Absol-Quests",
- // Alerts file location (used to structure how the message will look)
- "alerts": "default.json",
+ // Embeds file location (used to structure how the message will look)
+ "embeds": "default.json",
// Alarm filters
"filters":"quests_absol.json",
// Either the geofence file path (`geojson` or `ini` format) or the geofence name
diff --git a/docs/config/config.md b/docs/config/config.md
new file mode 100644
index 00000000..9d7cf30c
--- /dev/null
+++ b/docs/config/config.md
@@ -0,0 +1,691 @@
+# Configuration
+
+At a minimum you'll want to make sure you have your webhook listening port set as well as one Discord server added to the `servers` property.
+
+## Full Config Example
+```json
+{
+ // Http listening interface for raw webhook data, use "*" to listen on all interfaces.
+ "host": "*",
+ // Http listener port for raw webhook data.
+ "port": 8008,
+ // Locale language translation
+ "locale": "en",
+ // Telemetry reporting
+ "sentry": true,
+ // yourls.org API
+ "shortUrlApi": {
+ // Determines whether the Short URL API is used or not
+ "enabled": false,
+ // ShortURL API (i.e. `https://domain.com/yourls-api.php`)
+ "apiUrl": "https://domain.com/u/api.php",
+ // ShortURL passwordless authentication signature
+ "signature": ""
+ },
+ "stripeApi": {
+ "apiKey": ""
+ },
+ // List of Discord servers to connect and post webhook messages to.
+ "servers": {
+ // Discord server #1 guild ID (replace `000000000000000123` with
+ // actual guild id of server)
+ "000000000000000123": "discord1.example.json",
+ // 2nd Discord server section (if applicable)
+ "000000000000000456": "discord2.example.json"
+ },
+ // Database configuration
+ "database": {
+ // Database to store notification subscriptions.
+ "main": {
+ // Database hostname or IP address.
+ "host": "127.0.0.1",
+ // Database connection port.
+ "port": 3306,
+ // Database user account name.
+ "username": "root",
+ // Database user account password.
+ "password": "password",
+ // Brock database name.
+ "database": "brockdb"
+ },
+ "scanner": {
+ // Database hostname or IP address.
+ "host": "127.0.0.1",
+ // Database connection port.
+ "port": 3306,
+ // Database user account name.
+ "username": "root",
+ // Database user account password.
+ "password": "password",
+ // RDM database name.
+ "database": "rdmdb"
+ },
+ "nests": {
+ // Database hostname or IP address.
+ "host": "127.0.0.1",
+ // Database connection port.
+ "port": 3306,
+ // Database user account name.
+ "username": "root",
+ // Database user account password.
+ "password": "password",
+ // PMSF manual nests database name.
+ "database": "manualdb"
+ }
+ },
+ "eventPokemon": {
+ // Determines if filtering event Pokemon is enabled or not.
+ "enabled": false,
+ // List of Pokemon IDs to treat as event and restrict postings and subscriptions to 90% IV or higher. (Filled in automatically with `event set` command)
+ "pokemonIds": [],
+ // Minimum IV value for an event Pokemon to have to meet in order to post via Discord channel alarm or direct message subscription.
+ "eventMinimumIV": 90,
+ // Event Pokemon filtering type
+ "type": "Include",
+ // Ignore event Pokemon if missing IV stats
+ "ignoreMissingStats": true
+ },
+ // URL config
+ "urls": {
+ // Scanner map url DTS option for embeds as `scanmaps_url`.
+ // {0} and {1} are placeholders to construct the url with latitude
+ // and longitude coordinates
+ "scannerMap": "http://map.example.com/@/{0}/{1}/15"
+ },
+ // Available icon styles
+ "iconStyles": {
+ // Default icon style
+ "Default": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Default",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/WatWowMap/wwm-uicons/main/"
+ },
+ // Pokemon icon type object to modify
+ "Pokemon": {
+ // Icon type display name
+ "name": "Default_Pokemon",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/nileplumb/PkmnShuffleMap/master/UICONS/pokemon/"
+ }
+ /*
+ "Raid",
+ "Egg",
+ "Gym",
+ "Pokestop",
+ "Reward",
+ "Invasion",
+ "Type",
+ "Nest",
+ "Team",
+ "Weather",
+ "Misc",
+ */
+ },
+ // Pokemon Home Icons
+ "Home": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Home",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/nileplumb/PkmnHomeIcons/master/UICONS_OS/"
+ }
+ },
+ // Pokemon Shuffle Icons
+ "Shuffle": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Shuffle",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/nileplumb/PkmnShuffleMap/master/UICONS/"
+ }
+ },
+ // Pokemon Go Application Icons
+ "Pokemon Go": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Pokemon Go",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/whitewillem/PogoAssets/main/uicons/"
+ }
+ },
+ // PMSF Icons
+ "PMSF": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "PMSF",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/pmsf/PMSF/develop/static/sprites/"
+ }
+ }
+ },
+ // Custom static map template files for each alarm type
+ "staticMaps": {
+ // Base url for static map service
+ "url": "http://tiles.example.com:8080",
+ // StaticMap or MultiStaticMap
+ "type": "StaticMap",
+ // Include nearby gyms with static map image
+ "includeGyms": false,
+ // Include nearby pokestops with static map image
+ "includePokestops": false,
+ // Including Gyms and Pokestops on the StaticMap only works if `pregenerate` is set to `true`
+ "pregenerate": true
+ },
+ // Get text message alerts with Twilio.com
+ "twilio": {
+ // Determines if text message alerts are enabled
+ "enabled": false,
+ // Twilio account SID (Get via Twilio dashboard)
+ "accountSid": "",
+ // Twilio account auth token (Get via Twilio dashboard)
+ "authToken": "",
+ // Twilio phone number that will be sending the text message alert
+ "from": "",
+ // List of Discord user ids that can receive text message alerts
+ "userIds": [],
+ // List of Discord roles that can receive text message alerts
+ "roleIds": [],
+ // List of acceptable Pokemon to receive text message alerts for
+ "pokemonIds": [201, 480, 481, 482, 443, 444, 445, 633, 634, 635, 610, 611, 612],
+ // Minimum acceptable IV value for Pokemon if not ultra rare (Unown, Lake Trio)
+ "minIV": 100
+ },
+ "reverseGeocoding": {
+ // Reverse geocoding provider
+ "provider": "osm", // osm/gmaps
+ // Cache reverse geocoding responses to disk to reduce request count
+ "cacheToDisk": true,
+ // Google Maps reverse geocoding
+ "gmaps": {
+ // Google maps key for reverse geocoding
+ "key": "",
+ // Google maps template schema for embeds
+ "schema": "{{Results.[0].FormattedAddress}}"
+ },
+ // OpenStreetMaps Nominatim reverse geocoding
+ "nominatim": {
+ // OSM Nominatim endpoint
+ "endpoint": "",
+ // OSM Nominatim template schema for embeds
+ "schema": "{{Address.Road}} {{Address.State}} {{Address.Postcode}} {{Address.Country}}"
+ }
+ },
+ // Minimum despawn time in minutes a Pokemon must have in order to send the alarm (default: 5 minutes)
+ "despawnTimeMinimumMinutes": 5,
+ // Reload subscriptions every minute to sync with WhMgr-UI changes
+ "reloadSubscriptionChangesMinutes": 1,
+ // Check for duplicate webhooks
+ "checkForDuplicates": true,
+ // Log webhook payloads to a file for debugging (do not enable unless you're having issues receiving data
+ "debug": false,
+ /*
+ * Only show logs with higher or equal priority levels:
+ * Trace: 0
+ * Debug: 1
+ * Info: 2
+ * Warning: 3
+ * Error: 4
+ * Critical: 5
+ * None: 6
+ */
+ "logLevel": 0,
+ // Acceptable and interested PVP leagues to parse.
+ "pvpLeagues": {
+ // League name key to match webhook key for PVP ranks.
+ "little": {
+ // League minimum acceptable CP
+ "minCP": 450,
+ // League maximum acceptable CP
+ "maxCP": 500,
+ // League minimum rank to meet
+ "minRank": 1,
+ // League maximum rank to meet
+ "maxRank": 100
+ },
+ "great": {
+ "minCP": 1400,
+ "maxCP": 1500,
+ "minRank": 1,
+ "maxRank": 100
+ },
+ "ultra": {
+ "minCP": 2400,
+ "maxCP": 2500,
+ "minRank": 1,
+ "maxRank": 100
+ }
+ }
+}
+```
+
+## Top Level
+```json
+{
+ // Http listening interface for raw webhook data, use "*" to listen on all interfaces.
+ "host": "*",
+ // Http listener port for raw webhook data.
+ "port": 8008,
+ // Locale language translation
+ "locale": "en",
+ // Minimum despawn time in minutes a Pokemon must have in order to send the alarm (default: 5 minutes)
+ "despawnTimeMinimumMinutes": 5,
+ // Reload subscriptions every minute to sync with WhMgr-UI changes
+ "reloadSubscriptionChangesMinutes": 1,
+ // Check for duplicate webhooks
+ "checkForDuplicates": true,
+ // Log webhook payloads to a file for debugging (do not enable unless you're having issues receiving data)
+ "debug": false,
+ /*
+ * Only show logs with higher or equal priority levels:
+ * Trace: 0
+ * Debug: 1
+ * Info: 2
+ * Warning: 3
+ * Error: 4
+ * Critical: 5
+ * None: 6
+ */
+ "logLevel": 0 "logLevel": 0,
+ // Acceptable and interested PVP leagues to parse.
+ "pvpLeagues": {
+ // League name key to match webhook key for PVP ranks.
+ "little": {
+ // League minimum acceptable CP
+ "minCP": 450,
+ // League maximum acceptable CP
+ "maxCP": 500,
+ // League minimum rank to meet
+ "minRank": 1,
+ // League maximum rank to meet
+ "maxRank": 100
+ },
+ "great": {
+ "minCP": 1400,
+ "maxCP": 1500,
+ "minRank": 1,
+ "maxRank": 100
+ },
+ "ultra": {
+ "minCP": 2400,
+ "maxCP": 2500,
+ "minRank": 1,
+ "maxRank": 100
+ }
+ }
+}
+```
+
+## Discord Servers
+```json
+{
+ // List of Discord servers to connect and post webhook messages to.
+ "servers": {
+ // Discord server #1 guild ID (replace `000000000000000123` with
+ // actual guild id of server)
+ "000000000000000123": "discord1.example.json",
+ // 2nd Discord server section (if applicable)
+ "000000000000000456": "discord2.example.json"
+ }
+}
+```
+
+## Short Url API
+```json
+{
+ // yourls.org API
+ "shortUrlApi": {
+ // Determines whether the Short URL API is used or not
+ "enabled": false,
+ // ShortURL API (i.e. `https://domain.com/yourls-api.php`)
+ "apiUrl": "https://domain.com/u/api.php",
+ // ShortURL passwordless authentication signature
+ "signature": ""
+ }
+}
+```
+
+## Stripe API
+```json
+{
+ "stripeApi": {
+ // Stripe API key (Stripe production API key, i.e. rk_3824802934
+ "apiKey": "",
+ }
+}
+```
+
+## Database Schemas
+```json
+{
+ // Database configuration
+ "database": {
+ // Database to store notification subscriptions.
+ "main": {
+ // Database hostname or IP address.
+ "host": "127.0.0.1",
+ // Database connection port.
+ "port": 3306,
+ // Database user account name.
+ "username": "root",
+ // Database user account password.
+ "password": "password",
+ // Brock database name.
+ "database": "brockdb"
+ },
+ "scanner": {
+ // Database hostname or IP address.
+ "host": "127.0.0.1",
+ // Database connection port.
+ "port": 3306,
+ // Database user account name.
+ "username": "root",
+ // Database user account password.
+ "password": "password",
+ // RDM database name.
+ "database": "rdmdb"
+ },
+ "nests": {
+ // Database hostname or IP address.
+ "host": "127.0.0.1",
+ // Database connection port.
+ "port": 3306,
+ // Database user account name.
+ "username": "root",
+ // Database user account password.
+ "password": "password",
+ // PMSF manual nests database name.
+ "database": "manualdb"
+ }
+ }
+}
+```
+
+## URLs
+```json
+{
+ // URL config
+ "urls": {
+ // Scanner map url DTS option for embeds as `scanmaps_url`.
+ // {0} and {1} are placeholders to construct the url with latitude
+ // and longitude coordinates
+ "scannerMap": "https://map.example.com/@/{0}/{1}/15"
+ }
+}
+```
+
+## Twilio Text Message Notifications
+```json
+{
+ // Get text message alerts with Twilio.com
+ "twilio": {
+ // Determines if text message alerts are enabled
+ "enabled": false,
+ // Twilio account SID (Get via Twilio dashboard)
+ "accountSid": "",
+ // Twilio account auth token (Get via Twilio dashboard)
+ "authToken": "",
+ // Twilio phone number that will be sending the text message alert
+ "from": "",
+ // List of Discord user ids that can receive text message alerts
+ "userIds": [],
+ // List of Discord roles that can receive text message alerts
+ "roleIds": [],
+ // List of acceptable Pokemon to receive text message alerts for
+ "pokemonIds": [201, 480, 481, 482, 443, 444, 445, 633, 634, 635, 610, 611, 612],
+ // Minimum acceptable IV value for Pokemon if not ultra rare (Unown, Lake Trio)
+ "minIV": 100
+ }
+}
+```
+
+## Icon Styles
+```json
+{
+ // Available icon styles
+ "iconStyles": {
+ // Default icon style
+ "Default": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Default",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/WatWowMap/wwm-uicons/main/"
+ },
+ // Pokemon icon type object to modify
+ "Pokemon": {
+ // Icon type display name
+ "name": "Default_Pokemon",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/nileplumb/PkmnShuffleMap/master/UICONS/pokemon/"
+ }
+ /*
+ "Raid",
+ "Egg",
+ "Gym",
+ "Pokestop",
+ "Reward",
+ "Invasion",
+ "Type",
+ "Nest",
+ "Team",
+ "Weather",
+ "Misc",
+ */
+ },
+ // Pokemon Home Icons
+ "Home": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Home",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/nileplumb/PkmnHomeIcons/master/UICONS_OS/"
+ }
+ },
+ // Pokemon Shuffle Icons
+ "Shuffle": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Shuffle",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/nileplumb/PkmnShuffleMap/master/ICONS_STANDARD/"
+ }
+ },
+ // Pokemon Go Application Icons
+ "Pokemon Go": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "Pokemon Go",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/whitewillem/PogoAssets/resized/icons_large-uicons"
+ }
+ },
+ // PokeDave Pokemon Shuffle Icons
+ "PokeDave Shuffle": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "PokeDave Shuffle",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/jepke/pokedave_shuffle_icons_-PMSF-/master/UICONS/"
+ }
+ },
+ // PMSF Icons
+ "PMSF": {
+ // Base icon type object to apply to all other icon types
+ "Base": {
+ // Icon type display name
+ "name": "PMSF",
+ // Icon type url path
+ "path": "https://raw.githubusercontent.com/pmsf/PMSF/develop/static/sprites/"
+ }
+ }
+ },
+}
+```
+
+## Static Map Templates
+```json
+{
+ // Custom static map template files for each alarm type
+ "staticMaps": {
+ // Static map template for Pokemon
+ "pokemon": {
+ // Static map url template for pokemon
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}",
+ // Static map template file name without extension
+ "template": "pokemon.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ },
+ // Static map template for Raids and Eggs
+ "raids": {
+ // Static map url template for raids
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}&team_id={{team_id}}",
+ // Static map template file name without extension
+ "template": "raids.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ },
+ // Static map template for Gym team control changes
+ "gyms": {
+ // Static map url template for gyms
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}&team_id={{team_id}}",
+ // Static map template file name without extension
+ "template": "gyms.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ },
+ // Static map template for field research quests
+ "quests": {
+ // Static map url template for quests
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}",
+ // Static map template file name without extension
+ "template": "quests.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ },
+ // Static map template for Team Rocket invasions
+ "invasions": {
+ // Static map url template for invasions
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}",
+ // Static map template file name without extension
+ "template": "invasions.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ },
+ // Static map template for Pokestop lures
+ "lures": {
+ // Static map url template for lures
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}",
+ // Static map template file name without extension
+ "template": "lures.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ },
+ // Static map template for weather changes
+ "weather": {
+ // Static map url template for weather
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}&polygon={{polygon}}",
+ // Static map template file name without extension
+ "template": "weather.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ },
+ // Static map template for nest postings
+ "nests": {
+ // Static map url template for nests
+ "url": "http://tiles.example.com/staticmap/{{template_name}}?lat={{lat}}&lon={{lon}}&url2={{url2}}&polygon={{polygon}}",
+ // Static map template file name without extension
+ "template": "nests.example",
+ // Include nearby gyms in static map image
+ "includeGyms": false,
+ // Include nearby pokestops in static map image
+ "includePokestops": false
+ }
+ }
+}
+```
+
+## Reverse Geocoding
+```json
+{
+ // Reverse lookup of geocoordinates to physical address
+ "reverseGeocoding": {
+ // Reverse geocoding provider
+ "provider": "osm", // osm/gmaps
+ // Cache reverse geocoding responses to disk to reduce request count
+ "cacheToDisk": true,
+ // Google Maps reverse geocoding
+ "gmaps": {
+ // Google maps key for reverse geocoding
+ "key": "",
+ // Google maps template schema for embeds
+ "schema": "{{Results.[0].FormattedAddress}}"
+ },
+ // OpenStreetMaps Nominatim reverse geocoding
+ "nominatim": {
+ // OSM Nominatim endpoint
+ "endpoint": "",
+ // OSM Nominatim template schema for embeds
+ "schema": "{{Address.Road}} {{Address.State}} {{Address.Postcode}} {{Address.Country}}"
+ }
+ }
+}
+```
+
+## PVP Leagues
+```json
+{
+ // Acceptable and interested PVP leagues to parse.
+ "pvpLeagues": {
+ // League name key to match webhook key for PVP ranks.
+ "little": {
+ // League minimum acceptable CP
+ "minCP": 450,
+ // League maximum acceptable CP
+ "maxCP": 500,
+ // League minimum rank to meet
+ "minRank": 1,
+ // League maximum rank to meet
+ "maxRank": 100
+ },
+ "great": {
+ "minCP": 1400,
+ "maxCP": 1500,
+ "minRank": 1,
+ "maxRank": 100
+ },
+ "ultra": {
+ "minCP": 2400,
+ "maxCP": 2500,
+ "minRank": 1,
+ "maxRank": 100
+ }
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/config/discords.md b/docs/config/discords.md
new file mode 100644
index 00000000..eb9c1b1b
--- /dev/null
+++ b/docs/config/discords.md
@@ -0,0 +1,137 @@
+# Discord Server Configs
+Copy your Discord specific configs to the `bin/discords` folder and reference them in the main config under the servers section.
+
+```json
+{
+ // Discord bot general config options
+ "bot": {
+ // Bot command prefix, leave blank to use @mention
+ "commandPrefix": ".",
+ // Discord guild ID.
+ "guildId": 000000000000000000,
+ // Discord Emoji server ID. (Can be same as `guildId`)
+ "emojiGuildId": 000000000000000001,
+ // Discord bot token with user.
+ "token": "",
+ // Channel ID(s) bot commands can be executed in. (currently not implemented)
+ "channelIds": [],
+ // Custom Discord status per server, leave blank or null to use current version.
+ "status": null
+ },
+ // Discord server owner ID.
+ "ownerId": 000000000000000000,
+ // Donor/Supporter role ID(s) config.
+ "donorRoleIds": {
+ // Discord server role id and subscription permissions
+ "000000000000000000": ["pokemon", "pvp", "raids", "quests", "invasions", "lures", "gyms"],
+ // User has access to nothing
+ "000000000000000001": [],
+ // Users with role will only have access to Pokestops and Gyms
+ "000000000000000002": ["quests", "gyms"]
+ },
+ // Discord free role name, if set allows non-donors/supporters to use the .feedme commands to assign city roles (optional, good for free promotional periods)
+ "freeRoleName": "",
+ // Moderator role IDs
+ "moderatorRoleIds": [
+ 000000000000000001,
+ 000000000000000002
+ ],
+ // Discord alarms config file name to use
+ "alarms": "alarms.json",
+ // Discord server related geofences
+ "geofences": [
+ "City1.txt",
+ "City2.json"
+ ],
+ // Subscriptions config
+ "subscriptions": {
+ // Determines whether subscriptions are enabled for the Discord server or not.
+ "enabled": false,
+ // Maximum notifications per minutes per subscriber before rate limited.
+ "maxNotificationsPerMinute": 10,
+ // Maximum Pokemon subscriptions in total
+ "maxPokemonSubscriptions": 0,
+ // Maximum PvP subscriptions in total
+ "maxPvPSubscriptions": 0,
+ // Maximum Raid subscriptions in total
+ "maxRaidSubscriptions": 0,
+ // Maximum Quest subscriptions in total
+ "maxQuestSubscriptions": 0,
+ // Maximum Invasion subscriptions in total
+ "maxInvasionSubscriptions": 0,
+ // Maximum Lure subscriptions in total
+ "maxLureSubscriptions": 0,
+ // Maximum Gym subscriptions in total
+ "maxGymSubscriptions": 0,
+ // Webhook Manager UI home page url
+ "url": "http://127.0.0.1:8009",
+ // Subscriptions DM embeds file.
+ "embedsFile": "default.json"
+ },
+ // Discord geofence roles config
+ "geofenceRoles": {
+ // Determines whether assignable/unassignable geofence roles for donors of the server are enabled
+ "enabled": false,
+ // Determines whether access removed automatically removes assigned geofence roles (highly recommended)
+ "autoRemove": true,
+ // Assigning geofence city roles requires donor/supporter role
+ "requiresDonorRole": true
+ },
+ // Automatic quest alarms purge from Discord channels based on timezones at midnight
+ "questsPurge": {
+ // Enables quest alarm messages purge from Discord channels
+ "enabled": false,
+ // Channels based on timezone
+ "channels": {
+ // Denver Timezone
+ "America/Denver": [
+ // Channel 1...
+ 000000000000000000,
+ // Channel 2...
+ 000000000000000001
+ ],
+ // New York timezone
+ "America/New_York": [
+ // Channel 1...
+ 000000000000000000,
+ // Channel 2...
+ 000000000000000001
+ ]
+ }
+ },
+ // Nest postings config
+ "nests": {
+ // Determines whether nest posting is enabled.
+ "enabled": false,
+ // Channel id to post nest postings to.
+ "channelId": 0,
+ // Minimum amount per hour to post nest posting.
+ "minimumPerHour": 2
+ },
+ // Daily stats config
+ "dailyStats": {
+ // Shiny stats config
+ "shiny": {
+ // Determines whether to post shiny stats or not
+ "enabled": false,
+ // Clear messages before posting
+ "clearMessages": false,
+ // Channel ID for posting shiny stats
+ "channelId": 0
+ },
+ // IV stats config
+ "iv": {
+ // Determines whether to post IV stats or not
+ "enabled": false,
+ // Clear messages before posting
+ "clearMessages": false,
+ // Channel ID for posting IV stats
+ "channelId": 0
+ }
+ },
+ // Icon style for postings from Discord server.
+ "iconStyle": "Default",
+ // Discord client log level, only change if debugging an issue with Discord (default: 4 aka Error)
+ "logLevel": 4
+}
+```
\ No newline at end of file
diff --git a/docs/config/embeds.md b/docs/config/embeds.md
new file mode 100644
index 00000000..e8b0105b
--- /dev/null
+++ b/docs/config/embeds.md
@@ -0,0 +1,269 @@
+# Embeds
+Embeds depict how Discord embed messages are formatted. Customization is endless.
+
+`` - Replacement placeholders.
+`<#condition>` - Conditional replacements.
+
+**Replacement Placeholders**
+Placeholders are used to build a template (similar to [mustache](https://mustache.github.io/)) which are replaced with real values from incoming webhooks and used to send outgoing Discord messages.
+
+**Conditional replacements**
+Enable the ability to only show something if the conditional value evaluates to `true`. A prime example would be if the Pokemon is near a Pokestop, to include the Pokestop name and image. Below is an example of it:
+```
+{{#if near_pokestop}}**Near Pokestop:** [{{pokestop_name}}]({{pokestop_url}}){{br}}{{/if}}
+```
+
+`{{pokestop_name}}` - Replaced by the name of the nearby Pokestop.
+`{{pokestop_url}}` - Replaced by the image url of the nearby Pokestop.
+`{{br}}` - Replaced with a new line break to preserve readability and formatting.
+
+For a list of available dynamic text substitution/replacement options check out the [DTS](../dts/index.md) pages.
+
+
+
+## Embed Message Structures
+```json
+{
+ "pokemon": {
+ // Embed avatar icon url
+ "avatarUrl": "{{pkmn_img_url}}",
+ // Embed content text, each array item is treated as a new line break
+ "content": [
+ "{{pkmn_name}} {{form}}{{gender}} {{iv}} ({{atk_iv}}/{{def_iv}}/{{sta_iv}}) L{{lvl}}",
+ "**Despawn:** {{despawn_time}} ({{time_left}} left){{despawn_time_verified}}",
+ "**Details:** CP: {{cp}} IV: {{iv}} LV: {{lvl}}",
+ "**Size:** {{size}} | {{types_emoji}}{{#if has_weather}} | {{weather_emoji}}{{#if is_weather_boosted}} (Boosted){{/if}}{{/if}}",
+ "**Moveset:** {{moveset}}",
+ "{{#if near_pokestop}}**Near Pokestop:** [{{pokestop_name}}]({{pokestop_url}})",
+ "{{/if}}{{#if is_ditto}}**Catch Pokemon:** {{original_pkmn_name}}",
+ "{{/if}}{{#if has_capture_rates}}{{capture_1_emoji}} {{capture_1}}% {{capture_2_emoji}} {{capture_2}}% {{capture_3_emoji}} {{capture_3}}%",
+ "{{/if}}{{#if is_event}}Go Fest Spawn",
+ "{{/if}}{{#if has_pvp}}",
+ "{{#each pvp}}**{{@key}}**",
+ "{{#each this}}",
+ "#{{rank}} {{getPokemonName pokemonId}} {{getFormName formId}} {{cp}}CP @ L{{level}} {{formatPercentage percentage}}%",
+ "{{/each}}{{/each}}",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ // Embed icon image url
+ "iconUrl": "{{pkmn_img_url}}",
+ // Embed title text
+ "title": "{{geofence}}",
+ // Embed title url
+ "url": "{{gmaps_url}}",
+ // Embed author username
+ "username": "{{form}} {{pkmn_name}}{{gender}}",
+ // Embed bottom image url
+ "imageUrl": "{{tilemaps_url}}",
+ // Embed footer
+ "footer": {
+ // Embed footer text
+ "text": "{{guild_name}} {{date_time}}",
+ // Embed footer icon url
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "pokemonMissingStats": {
+ "avatarUrl": "{{pkmn_img_url}}",
+ "content": [
+ "{{pkmn_name}} {{form}}{{gender}}",
+ "**Despawn:** {{despawn_time}} ({{time_left}} left){{despawn_time_verified}}",
+ "**Types:** {{types_emoji}}",
+ "{{#if near_pokestop}}**Near Pokestop:** [{{pokestop_name}}]({{pokestop_url}})",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pkmn_img_url}}",
+ "title": "{{geofence}}",
+ "url": "{{gmaps_url}}",
+ "username": "{{form}} {{pkmn_name}}{{gender}}",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "gyms": {
+ "avatarUrl": "{{gym_url}}",
+ "content": [
+ "{{#if team_changed}}Gym changed from {{old_gym_team_emoji}} {{old_gym_team}} to {{gym_team_emoji}} {{gym_team}}",
+ "{{/if}}{{#if in_battle}}Gym is under attack!",
+ "{{/if}}**Slots Available:** {{slots_available}}",
+ "{{#if power_up_level}}**Power Level**",
+ "Level: {{power_up_level}} | Points: {{power_up_points}}",
+ "Time Left: {{power_up_end_time_left}}",
+ "{{/if}}{{#if is_ex}}{{ex_gym_emoji}} Gym!",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{gym_url}}",
+ "title": "{{geofence}}: {{gym_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "{{gym_name}}",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "raids": {
+ "avatarUrl": "{{pkmn_img_url}}",
+ "content": [
+ "{{evolution}} {{form}} {{pkmn_name}}{{gender}} {{costume}} Raid Ends: {{end_time}} ({{end_time_left}} left)",
+ "**Perfect CP:** {{perfect_cp}} / :white_sun_rain_cloud: {{perfect_cp_boosted}}",
+ "**Worst CP:** {{worst_cp}} / :white_sun_rain_cloud: {{worst_cp_boosted}}",
+ "**Types:** {{types_emoji}} | **Level:** {{lvl}} | **Team:** {{team_emoji}}",
+ "**Moveset:** {{moveset}}",
+ "**Weaknesses:** {{weaknesses_emoji}}",
+ "{{#if is_ex}}{{ex_emoji}} Gym!",
+ "{{/if}}{{#if power_up_level}}**Power Level**",
+ "Level: {{power_up_level}} | Points: {{power_up_points}}",
+ "Time Left: {{power_up_end_time_left}}",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pkmn_img_url}}",
+ "title": "{{geofence}}: {{gym_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "{{form}} {{pkmn_name}}{{gender}} {{costume}} Raid",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "eggs": {
+ "avatarUrl": "{{pkmn_img_url}}",
+ "content": [
+ "Hatches: {{start_time}} ({{start_time_left}})",
+ "**Ends:** {{end_time}} ({{end_time_left}} left)",
+ "**Team:** {{team_emoji}}",
+ "{{#if is_ex}}{{ex_emoji}} Gym!",
+ "{{/if}}{{#if power_up_level}}**Power Level**",
+ "Level: {{power_up_level}} | Points: {{power_up_points}}",
+ "Time Left: {{power_up_end_time_left}}",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pkmn_img_url}}",
+ "title": "{{geofence}}: {{gym_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "Level {{lvl}} Egg",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "pokestops": {
+ "avatarUrl": "{{pokestop_url}}",
+ "content": [
+ "{{#if has_lure}}**Lure Expires** {{lure_expire_time}} ({{lure_expire_time_left}} left)",
+ "**Lure Type:** {{lure_type}}",
+ "{{/if}}{{#if power_up_level}}**Power Level**",
+ "Level: {{power_up_level}} | Points: {{power_up_points}}",
+ "Time Left: {{power_up_end_time_left}}",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pokestop_url}}",
+ "title": "{{geofence}}: {{pokestop_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "{{pokestop_name}}",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "quests": {
+ "avatarUrl": "{{quest_reward_img_url}}",
+ "content": [
+ "**Quest:** {{quest_task}}",
+ "{{#if has_quest_conditions}}**Condition(s):** {{quest_conditions}}",
+ "{{/if}}**Reward:** {{quest_reward}}",
+ "{{#if is_ar}}**AR Quest Required!**",
+ "{{/if}}",
+ "**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pokestop_url}}",
+ "title": "{{geofence}}: {{pokestop_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "{{quest_task}}",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "lures": {
+ "avatarUrl": "{{lure_img_url}}",
+ "content": [
+ "{{#if has_lure}}**Lure Expires** {{lure_expire_time}} ({{lure_expire_time_left}} left)",
+ "**Lure Type:** {{lure_type}}",
+ "{{/if}}{{#if power_up_level}}**Power Level**",
+ "Level: {{power_up_level}} | Points: {{power_up_points}}",
+ "Time Left: {{power_up_end_time_left}}",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pokestop_url}}",
+ "title": "{{geofence}}: {{pokestop_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "{{pokestop_name}}",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "invasions": {
+ "avatarUrl": "{{invasion_img_url}}",
+ "content": [
+ "{{#if has_invasion}}**Expires:** {{invasion_expire_time}} ({{invasion_expire_time_left}} left)",
+ "**Type:** {{grunt_type_emoji}} | **Gender:** {{grunt_gender}}",
+ "**Encounter Reward Chance:**",
+ "{{#each invasion_encounters}}",
+ "{{chance}} - {{pokemon}}",
+ "{{/each}}",
+ "{{/if}}**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pokestop_url}}",
+ "title": "{{geofence}}: {{pokestop_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "{{pokestop_name}}",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "nests": {
+ "avatarUrl": "{{pkmn_img_url}}",
+ "content": [
+ "**Pokemon:** {{pkmn_name}}",
+ "**Average Spawns:** {{avg_spawns}}/h | **Types:** {{types_emojis}}",
+ "**[Google]({{gmaps_url}}) | [Apple]({{applemaps_url}}) | [Waze]({{wazemaps_url}}) | [Scanner]({{scanmaps_url}})**"
+ ],
+ "iconUrl": "{{pkmn_img_url}}",
+ "title": "{{geofence}}: {{nest_name}}",
+ "url": "{{gmaps_url}}",
+ "username": "",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ },
+ "weather": {
+ "avatarUrl": "{{weather_img_url}}",
+ "content": [
+ "**Weather Condition:** {{weather_emoji}} {{weather_condition}}",
+ "**Weather Cell ID:** #{{id}}"
+ ],
+ "iconUrl": "{{weather_img_url}}",
+ "title": "{{geofence}}",
+ "url": "{{gmaps_url}}",
+ "username": "Weather",
+ "imageUrl": "{{tilemaps_url}}",
+ "footer": {
+ "text": "{{guild_name}} {{date_time}}",
+ "iconUrl": "{{guild_img_url}}"
+ }
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/config/filters.md b/docs/config/filters.md
new file mode 100644
index 00000000..177b3794
--- /dev/null
+++ b/docs/config/filters.md
@@ -0,0 +1,232 @@
+# Filters
+Filters allow you to narrow down what is reported. All filters are optional and can be omitted. Plenty of examples in the repository under the [`examples/Filters`](https://github.com/versx/WhMgr/tree/master/examples/filters) directory for all different needs.
+
+## Filter Converters
+- WDR [Filter Converter](https://github.com/versx/WdrFilterConverter)
+
+## Available Filter Options
+```json
+{
+ "pokemon":
+ {
+ "enabled": true, // Filter is enabled
+ "pokemon": [280,337,374], // List of Pokemon for the filter or empty for all
+ "forms": ["Alola", "Galar"],
+ "costumes": ["Detective","Holiday"], // List of costumes for the filter or empty for all
+ "min_iv": 0, // Minimum IV of Pokemon to send
+ "max_iv": 100, // Maximum IV of Pokemon to send
+ "min_cp": 0, // Minimum CP of Pokemon
+ "max_cp": 999999, // Maximum CP of Pokemon
+ "min_lvl": 0, // Minimum level of Pokemon
+ "max_lvl": 35, // Maximum level of Pokemon
+ "gender": "m", // Only send male (m,f,*)
+ "size": "Big", // Tiny, Small, Normal, Large, Big
+ // Add or remove any PVP league filtering keys
+ // depending on the interested ranks.
+ "pvp": {
+ // Little league rank filtering
+ "little": {
+ // Minimum rank of #1 for PVP rank stats
+ "min_rank": 1,
+ // Maximum rank of #5 for PVP rank stats
+ "max_rank": 5,
+ // Minimum CP value of 400 for PVP rank stats
+ "min_cp": 400,
+ // Maximum CP value of 500 for PVP rank stats
+ "max_cp": 500,
+ // Minimum PVP product stat
+ "min_percent": 90,
+ // Maximum PVP product stat
+ "max_percent": 100,
+ // Gender filtering requirement (*, m, or f)
+ "gender": "*"
+ },
+ // Great league rank filtering
+ "great": {
+ "min_rank": 1,
+ "max_rank": 5,
+ "min_cp": 1400,
+ "max_cp": 1500,
+ "gender": "m"
+ },
+ // Ultra league rank filtering
+ "ultra": {
+ "min_rank": 1,
+ "max_rank": 25,
+ "min_cp": 2400,
+ "max_cp": 2500,
+ "gender": "f"
+ }
+ },
+ "type": "Include", // Include or Exclude the `pokemon` list
+ "is_event": false, // Only send Pokemon checked with event accounts (GoFest, etc)
+ "ignore_missing": true // Ignore Pokemon missing stats
+ },
+ "eggs":
+ {
+ "enabled": true, // Filter is enabled
+ "min_lvl": 1, // Minimum egg level to send
+ "max_lvl": 8, // Maximum egg level to send
+ "only_ex": false, // Only send ex-eligible raids.
+ "team": "All", // All, Valor, Mystic, Instinct, Neutral
+ "power_level": {
+ "min_level": 1,
+ "max_level": 3,
+ "min_points": 10,
+ "max_points": 250
+ }
+ },
+ "raids":
+ {
+ "enabled": true, // Filter is enabled
+ "pokemon": [], // Raid bosses to include or none for all.
+ "forms": ["Alola","Galar"], // List of forms for the filter or empty for all
+ "costumes": ["Detective","Holiday"], // List of costumes for the filter or empty for all
+ "min_lvl": 1, // Minimum raid level to send
+ "max_lvl": 8, // Maximum raid level to send
+ "type": "Include", // Include or Exclude the `pokemon` list
+ "only_ex": false, // Only send ex-eligible raids.
+ "team": "All", // All, Valor, Mystic, Instinct, Nuetral
+ "power_level": {
+ "min_level": 1,
+ "max_level": 3,
+ "min_points": 10,
+ "max_points": 250
+ },
+ "ignore_missing": true // Ignore raids missing stats
+ },
+ "quests":
+ {
+ "enabled": true, // Filter is enabled
+ "rewards": ["spinda", "nincada"], // Quest reward string (Chansey, stardust, candy, etc.)
+ "is_shiny": false, // Only shiny encounter quests.
+ "type": "Include" // Include or Exclude the `rewards` list
+ },
+ "pokestops":
+ {
+ "enabled": true, // Filter is enabled
+ "lured": true, // Only send lured pokestops
+ "lure_types": ["Normal", "Glacial", "Mossy", "Magnetic"], // Only send lures in type list
+ "power_level": {
+ "min_level": 1,
+ "max_level": 3,
+ "min_points": 10,
+ "max_points": 250
+ }
+ },
+ "invasions": {
+ "enabled": true, // Filter is enabled
+ // Determines which invasion types to send
+ "invasionTypes": {
+ "CharacterUnset": false,
+ "CharacterBlanche": true,
+ "CharacterCandela": true,
+ "CharacterSpark": true,
+ "CharacterGruntMale": true,
+ "CharacterGruntFemale": true,
+ "CharacterBugGruntFemale": true,
+ "CharacterBugGruntMale": true,
+ "CharacterDarknessGruntFemale": true,
+ "CharacterDarknessGruntMale": true,
+ "CharacterDarkGruntFemale": true,
+ "CharacterDarkGruntMale": true,
+ "CharacterDragonGruntFemale": true,
+ "CharacterDragonGruntMale": true,
+ "CharacterFairyGruntFemale": true,
+ "CharacterFairyGruntMale": true,
+ "CharacterFightingGruntFemale": true,
+ "CharacterFightingGruntMale": true,
+ "CharacterFireGruntFemale": true,
+ "CharacterFireGruntMale": true,
+ "CharacterFlyingGruntFemale": true,
+ "CharacterFlyingGruntMale": true,
+ "CharacterGrassGruntFemale": true,
+ "CharacterGrassGruntMale": true,
+ "CharacterGroundGruntFemale": true,
+ "CharacterGroundGruntMale": true,
+ "CharacterIceGruntFemale": true,
+ "CharacterIceGruntMale": true,
+ "CharacterMetalGruntFemale": true,
+ "CharacterMetalGruntMale": true,
+ "CharacterNormalGruntFemale": true,
+ "CharacterNormalGruntMale": true,
+ "CharacterPoisonGruntFemale": true,
+ "CharacterPoisonGruntMale": true,
+ "CharacterPsychicGruntFemale": true,
+ "CharacterPsychicGruntMale": true,
+ "CharacterRockGruntFemale": true,
+ "CharacterRockGruntMale": true,
+ "CharacterWaterGruntFemale": true,
+ "CharacterWaterGruntMale": true,
+ "CharacterPlayerTeamLeader": true,
+ "CharacterExecutiveCliff": true,
+ "CharacterExecutiveArlo": true,
+ "CharacterExecutiveSierra": true,
+ "CharacterGiovanni": true,
+ "CharacterDecoyGruntMale": true,
+ "CharacterDecoyGruntFemale": true,
+ "CharacterGhostGruntFemale": true,
+ "CharacterGhostGruntMale": true,
+ "CharacterElectricGruntFemale": true,
+ "CharacterElectricGruntMale": true,
+ "CharacterBalloonGruntFemale": true,
+ "CharacterBalloonGruntMale": true,
+ "CharacterGruntbFemale": true,
+ "CharacterGruntbMale": true,
+ "CharacterBugBalloonGruntFemale": true,
+ "CharacterBugBalloonGruntMale": true,
+ "CharacterDarkBalloonGruntFemale": true,
+ "CharacterDarkBalloonGruntMale": true,
+ "CharacterDragonBalloonGruntFemale": true,
+ "CharacterDragonBalloonGruntMale": true,
+ "CharacterFairyBalloonGruntFemale": true,
+ "CharacterFairyBalloonGruntMale": true,
+ "CharacterFightingBalloonGruntFemale": true,
+ "CharacterFightingBalloonGruntMale": true,
+ "CharacterFireBalloonGruntFemale": true,
+ "CharacterFireBalloonGruntMale": true,
+ "CharacterFlyingBalloonGruntFemale": true,
+ "CharacterFlyingBalloonGruntMale": true,
+ "CharacterGrassBalloonGruntFemale": true,
+ "CharacterGrassBalloonGruntMale": true,
+ "CharacterGroundBalloonGruntFemale": true,
+ "CharacterGroundBalloonGruntMale": true,
+ "CharacterIceBalloonGruntFemale": true,
+ "CharacterIceBalloonGruntMale": true,
+ "CharacterMetalBalloonGruntFemale": true,
+ "CharacterMetalBalloonGruntMale": true,
+ "CharacterNormalBalloonGruntFemale": true,
+ "CharacterNormalBalloonGruntMale": true,
+ "CharacterPoisonBalloonGruntFemale": true,
+ "CharacterPoisonBalloonGruntMale": true,
+ "CharacterPsychicBalloonGruntFemale": true,
+ "CharacterPsychicBalloonGruntMale": true,
+ "CharacterRockBalloonGruntFemale": true,
+ "CharacterRockBalloonGruntMale": true,
+ "CharacterWaterBalloonGruntFemale": true,
+ "CharacterWaterBalloonGruntMale": true,
+ "CharacterGhostBalloonGruntFemale": true,
+ "CharacterGhostBalloonGruntMale": true,
+ "CharacterElectricBalloonGruntFemale": true,
+ "CharacterElectricBalloonGruntMale": true
+ }
+ },
+ "gyms":
+ {
+ "enabled": true, // Filter is enabled
+ "under_attack": true, // Only gyms that are under attack
+ "team": "All", // Team change to notify about (i.e. Neutral/Mystic/Valor/Instinct/All)
+ "power_level": {
+ "min_level": 1,
+ "max_level": 3,
+ "min_points": 10,
+ "max_points": 250
+ }
+ },
+ "weather":
+ {
+ "enabled": true, // Filter is enabled
+ "types": ["Clear", "Rainy", "PartlyCloudy", "Overcast", "Windy", "Snow", "Fog"] // Only send weather types that are in the list
+ }
+}
+```
diff --git a/docs/user-guide/geofences.md b/docs/config/geofences.md
similarity index 82%
rename from docs/user-guide/geofences.md
rename to docs/config/geofences.md
index 120c167b..f287062b 100644
--- a/docs/user-guide/geofences.md
+++ b/docs/config/geofences.md
@@ -1,89 +1,98 @@
-# Geofences
-
-Geofences define area borders and perimeters for a city or multiple cities. Each alarm can take multiple geofence files or names, as well as a combination of both.
-
-**Geofences must be placed in the `bin/geofences` folder.**
-*Note:* Supports INI geofence file format as well as GeoJSON geofence file format:
-
-## Examples
-
-## __INI Format__
-```ini
-[City1]
-34.00,-117.00
-34.01,-117.01
-34.02,-117.02
-34.03,-117.03
-[City2]
-33.00,-118.00
-33.01,-118.01
-33.02,-118.02
-33.03,-118.03
-```
-## __GeoJSON Format__
-```json
-{
- "type": "FeatureCollection",
- "features": [
- {
- "type": "Feature",
- "id": 12143584,
- "geometry": {
- "type": "Polygon",
- "coordinates": [
- [
- [
- -117.185508,
- 34.05361
- ],
- [
- -117.185397,
- 34.05366
- ],
- [
- -117.185326,
- 34.053564
- ],
- [
- -117.184819,
- 34.053828
- ],
- [
- -117.184457,
- 34.054009
- ],
- [
- -117.18409,
- 34.05353
- ],
- [
- -117.184027,
- 34.053448
- ],
- [
- -117.184991,
- 34.052942
- ],
- [
- -117.185508,
- 34.05361
- ]
- ]
- ]
- },
- "properties": {
- "name": "Unknown Areaname",
- "stroke": "#352BFF",
- "stroke-width": 2.0,
- "stroke-opacity": 1.0,
- "fill": "#0651FF",
- "fill-opacity": 0.5,
- "priority": 2,
- }
- }
- ]
-}
-```
-
-
-Optional: [**GeoJSON to individual INI format geofence files converter**](https://gist.github.com/versx/a0915c6bd95a080b6ff60cd539d4feb6)
\ No newline at end of file
+# Geofences
+
+Geofences define area borders and perimeters for a city or multiple cities. Each alarm can take multiple geofence files or names, as well as a combination of both.
+
+**Geofences must be placed in the `bin/geofences` folder.**
+*Note:* Supports INI geofence file format as well as GeoJSON geofence file format:
+
+## Examples
+
+### __INI Format__
+```ini
+[City1]
+34.00,-117.00
+34.01,-117.01
+34.02,-117.02
+34.03,-117.03
+[City2]
+33.00,-118.00
+33.01,-118.01
+33.02,-118.02
+33.03,-118.03
+```
+### __GeoJSON Format__
+```json
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "id": 12143584,
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ -117.185508,
+ 34.05361
+ ],
+ [
+ -117.185397,
+ 34.05366
+ ],
+ [
+ -117.185326,
+ 34.053564
+ ],
+ [
+ -117.184819,
+ 34.053828
+ ],
+ [
+ -117.184457,
+ 34.054009
+ ],
+ [
+ -117.18409,
+ 34.05353
+ ],
+ [
+ -117.184027,
+ 34.053448
+ ],
+ [
+ -117.184991,
+ 34.052942
+ ],
+ [
+ -117.185508,
+ 34.05361
+ ]
+ ]
+ ]
+ },
+ "properties": {
+ "enabled": true,
+ "name": "Unknown Areaname",
+ "stroke": "#352BFF",
+ "stroke-width": 2.0,
+ "stroke-opacity": 1.0,
+ "fill": "#0651FF",
+ "fill-opacity": 0.5,
+ "priority": 2
+ }
+ }
+ ]
+}
+```
+
+## __Converter Scripts__
+
+GeoJSON to individual INI format geofence files converter:
+https://gist.github.com/versx/a0915c6bd95a080b6ff60cd539d4feb6
+
+Combine all GeoJSON files to one single file:
+https://github.com/WatWowMap/MapJS/blob/master/src/geofenceToGeoJSON.js
+
+Poracle GeoJSON format to GeoJSON:
+https://github.com/WatWowMap/MapJS/blob/master/src/poracleToGeoJSON.js
\ No newline at end of file
diff --git a/docs/user-guide/dts/gyms.md b/docs/dts/gyms.md
similarity index 79%
rename from docs/user-guide/dts/gyms.md
rename to docs/dts/gyms.md
index afda2e2b..12196c51 100644
--- a/docs/user-guide/dts/gyms.md
+++ b/docs/dts/gyms.md
@@ -1,35 +1,42 @@
-# Dynamic Text Replacement
-
-Use any of the following in your alerts file to structure how notifications will look for Gyms.
-
-### Gyms
-
-| Place Holder | Description | Example
-|---|---|---|
-| gym_id | Gym ID | 032840982304982034.16
-| gym_name | Name of Gym | The Amazing Gym
-| gym_url | Image url of Gym | https://google.com/imgs/gym.png
-| gym_team | Current team that has gym control | Valor
-| gym_team_emoji | Emoji icon of current team that has gym control | <:09833:valor>
-| old_gym_team | Previous gym team that had gym control | Mystic
-| old_gym_team_emoji | Emoji icon of previous gym team that has gym control | <:324987:mystic>
-| team_changed | Returns if team's gym control changed | true
-| in_battle | Returns if there's a current battle at the gym taking place | false
-| under_attack | Returns if there's a current battle at the gym taking place | false
-| is_ex | Returns if the gym is an ex raid eligible location | true
-| ex_emoji | Ex emoji icon | <:809809:ex>
-| slots_available | Number of available gym slots | 3
-| geofence | Geofence name raid boss is in | City1
-| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
-| lat | Latitude coordinate of Pokemon location | 5.980921321
-| lng | Longitude coordinate of Pokemon location | 3.109283009
-| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
-| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
-| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
-| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
-| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
-| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
-| guild_name | Name of Guild | Test Guild
-| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
-| date_time | Current date and time | 12/12/2020 12:12:12 PM
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for Gyms.
+
+### Gyms
+
+| Place Holder | Description | Example
+|---|---|---|
+| gym_id | Gym ID | 032840982304982034.16
+| gym_name | Name of Gym | The Amazing Gym
+| gym_url | Image url of Gym | https://google.com/imgs/gym.png
+| gym_team | Current team that has gym control | Valor
+| gym_team_emoji | Emoji icon of current team that has gym control | <:09833:valor>
+| old_gym_team | Previous gym team that had gym control | Mystic
+| old_gym_team_emoji | Emoji icon of previous gym team that has gym control | <:324987:mystic>
+| team_changed | Returns if team's gym control changed | true
+| in_battle | Returns if there's a current battle at the gym taking place | false
+| under_attack | Returns if there's a current battle at the gym taking place | false
+| is_ex | Returns if the gym is an ex raid eligible location | true
+| ex_emoji | Ex emoji icon | <:809809:ex>
+| slots_available | Number of available gym slots | 3
+| sponsor_id | Some ID value I have no idea about | 33333
+| partner_id | Some ID value I have no idea about | 44444
+| power_up_level | Gym power level | 1
+| power_up_points | Gym's total power level points | 100
+| power_up_end_time | Gym's power up end time | 10:15:09 PM
+| power_up_end_time_24h | Gym's power up end time (24-hour format) | 13:28:30
+| power_up_end_time_left | Gym's power up time left until expires | 14m, 10s
+| geofence | Geofence name raid boss is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of Pokemon location | 5.980921321
+| lng | Longitude coordinate of Pokemon location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| guild_name | Name of Guild | Test Guild
+| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/dts/index.md b/docs/dts/index.md
new file mode 100644
index 00000000..eb03f535
--- /dev/null
+++ b/docs/dts/index.md
@@ -0,0 +1,8 @@
+# Dynamic Text Substitution/Replacement Types
+- [Pokemon](./pokemon.md)
+- [Raid & Eggs](./raids.md)
+- [Quests](./quests.md)
+- [Pokestops](./pokestops.md)
+- [Gyms](./gyms.md)
+- [Nests](./nests.md)
+- [Weather](./weather.md)
\ No newline at end of file
diff --git a/docs/user-guide/dts/pokestops.md b/docs/dts/invasions.md
similarity index 72%
rename from docs/user-guide/dts/pokestops.md
rename to docs/dts/invasions.md
index 9438fad6..f5387266 100644
--- a/docs/user-guide/dts/pokestops.md
+++ b/docs/dts/invasions.md
@@ -1,40 +1,38 @@
-# Dynamic Text Replacement
-
-Use any of the following in your alerts file to structure how notifications will look for Raids and Eggs.
-
-### Pokestops (Lures and Team Rocket invasions)
-
-| Place Holder | Description | Example
-|---|---|---|
-| has_lure | Returns if Pokestop has active lure module deployed | true
-| lure_type | Pokestop lure module type | Glacial
-| lure_expire_time | Time lure module will expire | 07:33:19 PM
-| lure_expire_time_24h | Time lure module will expire (24-hour format) | 19:33:19
-| lure_expire_time_left | Time left until lure module expires | 13m, 2s
-| has_invasion | Returns if Pokestop has active Team Rocket invasion | false
-| grunt_type | Grunt type | Water
-| grunt_type_emoji | Emoji icon of grunt type | <:938294:types_water>
-| grunt_gender | Grunt gender | Male
-| invasion_expire_time | Time the invasion expires | 02:17:11 PM
-| invasion_expire_time_24h | Time the invasion expires (24-hour format) | 14:17:11
-| invasion_expire_time_left | Time left until invasion expires | 12m, 56s
-| invasion_encounters | Possible invasions reward encounters | 80% Bulbasaur
-| geofence | Geofence name raid boss is in | City1
-| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
-| lat | Latitude coordinate of Pokemon location | 5.980921321
-| lng | Longitude coordinate of Pokemon location | 3.109283009
-| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
-| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
-| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
-| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
-| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
-| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
-| pokestop_id | Pokestop ID | 9382498723849792348798234.16
-| pokestop_name | Name of Pokestop | The Amazing Pokestop
-| pokestop_url | Image url of Gym | https://google.com/imgs/gym.png
-| lure_img_url | Image url of lure icon | https://google.com/imgs/lure_501.png
-| invasion_img_url | Image url of grunt type icon | https://google.com/imgs/grunt_50.png
-| guild_name | Name of Guild | Test Guild
-| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
-| date_time | Current date and time | 12/12/2020 12:12:12 PM
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for Team Rocket Invasions.
+
+### Team Rocket Invasions
+
+| Place Holder | Description | Example
+|---|---|---|
+| grunt_type | Grunt type | Water
+| grunt_type_emoji | Emoji icon of grunt type | <:938294:types_water>
+| grunt_gender | Grunt gender | Male
+| grunt_gender_id | Grunt gender id | 2
+| display_type | Incident type i.e. Grunt, Leader, etc | InvasionGrunt
+| display_type_id | Incident type id | 2
+| style | Incident style | PokestopNormal, PokestopRocketInvasion
+| style_id | Incident style id | 0
+| invasion_expire_time | Time the invasion expires | 02:17:11 PM
+| invasion_expire_time_24h | Time the invasion expires (24-hour format) | 14:17:11
+| invasion_expire_time_left | Time left until invasion expires | 12m, 56s
+| invasion_encounters | Possible invasions reward encounters | 80% Bulbasaur
+| geofence | Geofence name raid boss is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of Pokemon location | 5.980921321
+| lng | Longitude coordinate of Pokemon location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| pokestop_id | Pokestop ID | 9382498723849792348798234.16
+| pokestop_name | Name of Pokestop | The Amazing Pokestop
+| pokestop_url | Image url of Gym | https://google.com/imgs/gym.png
+| invasion_img_url | Image url of grunt type icon | https://google.com/imgs/grunt_50.png
+| guild_name | Name of Guild | Test Guild
+| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/user-guide/dts/nests.md b/docs/dts/nests.md
similarity index 94%
rename from docs/user-guide/dts/nests.md
rename to docs/dts/nests.md
index 0859a4d8..dd0d5e8d 100644
--- a/docs/user-guide/dts/nests.md
+++ b/docs/dts/nests.md
@@ -1,32 +1,32 @@
-# Dynamic Text Replacement
-
-Use any of the following in your alerts file to structure how notifications will look for nests.
-
-### Nests
-
-| Place Holder | Description | Example
-|---|---|---|
-| pkmn_id | Pokedex ID | 1
-| pkmn_id_3 | Pokedex ID (always 3 digits) | 001
-| pkmn_name | Pokemon name | Bulbasaur
-| pkmn_img_url | Pokemon image url | http://example.com/your-specified-pokemon-url
-| avg_spawns | Average amount of spawns in the nests | 34
-| nest_name | Nest/Park name | Best Park Ever
-| type_1 | Pokemon type | Dark
-| type_2 | Pokemon type | Water
-| type_1_emoji | Pokemon type emoji | <:00000:types_water>
-| type_2_emoji | Pokemon type emoji | <:00000:types_rock>
-| types | Both types (if 2nd exists) | Dark/Fire
-| types_emoji | Type Discord emoji | <:00000:types_fire> <00001:types_dark>
-| geofence | Geofence name nest/park is in | City1
-| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
-| lat | Latitude coordinate of Pokemon location | 5.980921321
-| lng | Longitude coordinate of S2Cell weather location | 3.109283009
-| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
-| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
-| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
-| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
-| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
-| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
-| date_time | Current date and time | 12/12/2020 12:12:12 PM
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for nests.
+
+### Nests
+
+| Place Holder | Description | Example
+|---|---|---|
+| pkmn_id | Pokedex ID | 1
+| pkmn_id_3 | Pokedex ID (always 3 digits) | 001
+| pkmn_name | Pokemon name | Bulbasaur
+| pkmn_img_url | Pokemon image url | http://example.com/your-specified-pokemon-url
+| avg_spawns | Average amount of spawns in the nests | 34
+| nest_name | Nest/Park name | Best Park Ever
+| type_1 | Pokemon type | Dark
+| type_2 | Pokemon type | Water
+| type_1_emoji | Pokemon type emoji | <:00000:types_water>
+| type_2_emoji | Pokemon type emoji | <:00000:types_rock>
+| types | Both types (if 2nd exists) | Dark/Fire
+| types_emoji | Type Discord emoji | <:00000:types_fire> <00001:types_dark>
+| geofence | Geofence name nest/park is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of Pokemon location | 5.980921321
+| lng | Longitude coordinate of S2Cell weather location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/user-guide/dts/pokemon.md b/docs/dts/pokemon.md
similarity index 92%
rename from docs/user-guide/dts/pokemon.md
rename to docs/dts/pokemon.md
index eab34b04..a05cbc6c 100644
--- a/docs/user-guide/dts/pokemon.md
+++ b/docs/dts/pokemon.md
@@ -1,79 +1,77 @@
-# Dynamic Text Replacement
-
-Use any of the following in your alerts file to structure how notifications will look for Pokemon.
-
-### Pokemon
-
-| Place Holder | Description | Example
-|---|---|---|
-| pkmn_id | Pokedex ID | 1
-| pkmn_id_3 | Pokedex ID (always 3 digits) | 001
-| pkmn_name | Pokemon name | Bulbasaur
-| pkmn_img_url | Pokemon image url | http://example.com/your-specified-pokemon-url
-| form | Pokemon form name | Alolan
-| form_id | Form ID | 65
-| form_id_3 | Form ID (always 3 digits) | 065
-| costume | Costume name | Witch Hat
-| costume_id | Costume ID | 835
-| costume_id_3 | Costume ID (always 3 digits) | 835
-| cp | Combat Power value | 1525
-| lvl | Pokemon level | 25
-| gender | Pokemon gender | Gender icon
-| gender_emoji | Pokemon gender emoji | <:00000:gender_male>
-| size | Pokemon size | Big
-| move_1 | Fast move name | Quick Attack
-| move_2 | Charge move name | Thunder
-| moveset | Fast & Charge move names | Quick Attack/Thunder
-| type_1 | Pokemon type | Dark
-| type_2 | Pokemon type | Water
-| type_1_emoji | Pokemon type emoji | <:00000:types_water>
-| type_2_emoji | Pokemon type emoji | <:00000:types_rock>
-| types | Both types (if 2nd exists) | Dark/Fire
-| types_emoji | Type Discord emoji | <:00000:types_fire> <00001:types_dark>
-| atk_iv | Attack IV stat | 15
-| def_iv | Defense IV stat | 7
-| sta_iv | Stamina IV stat | 13
-| iv | IV stat (including percent sign) | 100%
-| iv_rnd | Rounded IV stat | 96%
-| is_great | Great League stats (bool) | true
-| is_ultra | Ultra League stats (bool) | false
-| is_pvp | Has either Great or Ultra league stats | true
-| great_league_emoji | Great League emoji icon | <000000:league_great>
-| ultra_league_emoji | Ultra League emoji icon | <000000:league_ultra>
-| pvp_stats | PvP stat ranking strings |
-| height | Pokemon height | 0.79
-| weight | Pokemon weight | 116
-| is_ditto | Checks if Ditto | true
-| original_pkmn_id | Pokedex ID of Ditto disguise | 13
-| original_pkmn_id_3 | Pokedex ID of Ditto disguise (always 3 digits) | 013
-| original_pkmn_name | Pokemon name of Ditto diguise | Weedle
-| is_weather_boosted | Returns if Pokemon is weather boosted | true
-| has_weather | Returns if Pokemon data has weather | false
-| weather | Weather in-game name | PartlyCloudy
-| weather_emoji | Weather in-game emoji | Weather
-| username | Account username of account that found Pokemon | Frank0324
-| spawnpoint_id | Spawnpoint ID Pokemon near | 3920849203840983204980
-| encounter_id | Encounter ID of Pokemon | 392874987239487924
-| despawn_time | Pokemon despawn time | 07:33:01 PM
-| despawn_time_24h | Pokemon despawn time (24-hour format) | 19:33:01
-| despawn_time_verified | Indicates if time is confirmed or not | `~` for not verified
-| is_despawn_time_verified | Returns if despawn time is verified | true
-| time_left | Minutes and seconds of time left until despawn | 29m, 30s
-| geofence | Geofence name Pokemon is in | City1
-| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
-| lat | Latitude coordinate of Pokemon location | 5.980921321
-| lng | Longitude coordinate of Pokemon location | 3.109283009
-| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
-| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
-| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
-| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
-| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
-| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
-| near_pokestop | Returns if Pokemon is near a Pokestop | true
-| pokestop_id | Nearby Pokestop ID | 9382498723849792348798234.16
-| pokestop_name | Name of nearby Pokestop | The Amazing Pokestop
-| pokestop_url | Image url of nearby Pokestop | https://google.com/imgs/gym.png
-| guild_name | Name of Guild | Test Guild
-| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
-| date_time | Current date and time | 12/12/2020 12:12:12 PM
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for Pokemon.
+
+### Pokemon
+
+| Place Holder | Description | Example
+|---|---|---|
+| pkmn_id | Pokedex ID | 1
+| pkmn_id_3 | Pokedex ID (always 3 digits) | 001
+| pkmn_name | Pokemon name | Bulbasaur
+| pkmn_img_url | Pokemon image url | http://example.com/your-specified-pokemon-url
+| form | Pokemon form name | Alolan
+| form_id | Form ID | 65
+| form_id_3 | Form ID (always 3 digits) | 065
+| costume | Costume name | Witch Hat
+| costume_id | Costume ID | 835
+| costume_id_3 | Costume ID (always 3 digits) | 835
+| cp | Combat Power value | 1525
+| lvl | Pokemon level | 25
+| gender | Pokemon gender | Gender icon
+| gender_emoji | Pokemon gender emoji | <:00000:gender_male>
+| size | Pokemon size | Big
+| move_1 | Fast move name | Quick Attack
+| move_2 | Charge move name | Thunder
+| moveset | Fast & Charge move names | Quick Attack/Thunder
+| type_1 | Pokemon type | Dark
+| type_2 | Pokemon type | Water
+| types | Both Pokemon types (if 2nd exists) | Dark/Fire
+| types_emoji | Type Discord emoji | <:00000:types_fire> <00001:types_dark>
+| atk_iv | Attack IV stat | 15
+| def_iv | Defense IV stat | 7
+| sta_iv | Stamina IV stat | 13
+| iv | IV stat (including percent sign) | 100%
+| iv_rnd | Rounded IV stat | 96%
+| is_great | Great League stats (bool) | true
+| is_ultra | Ultra League stats (bool) | false
+| is_pvp | Has either Great or Ultra league stats | true
+| great_league_emoji | Great League emoji icon | <000000:league_great>
+| ultra_league_emoji | Ultra League emoji icon | <000000:league_ultra>
+| pvp_stats | PvP stat ranking strings |
+| height | Pokemon height | 0.79
+| weight | Pokemon weight | 116
+| is_ditto | Checks if Ditto | true
+| original_pkmn_id | Pokedex ID of Ditto disguise | 13
+| original_pkmn_id_3 | Pokedex ID of Ditto disguise (always 3 digits) | 013
+| original_pkmn_name | Pokemon name of Ditto diguise | Weedle
+| is_weather_boosted | Returns if Pokemon is weather boosted | true
+| has_weather | Returns if Pokemon data has weather | false
+| weather | Weather in-game name | PartlyCloudy
+| weather_emoji | Weather in-game emoji | Weather
+| username | Account username of account that found Pokemon | Frank0324
+| spawnpoint_id | Spawnpoint ID Pokemon near | 3920849203840983204980
+| encounter_id | Encounter ID of Pokemon | 392874987239487924
+| despawn_time | Pokemon despawn time | 07:33:01 PM
+| despawn_time_24h | Pokemon despawn time (24-hour format) | 19:33:01
+| despawn_time_verified | Indicates if time is confirmed or not | `~` for not verified
+| is_despawn_time_verified | Returns if despawn time is verified | true
+| time_left | Minutes and seconds of time left until despawn | 29m, 30s
+| geofence | Geofence name Pokemon is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of Pokemon location | 5.980921321
+| lng | Longitude coordinate of Pokemon location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| near_pokestop | Returns if Pokemon is near a Pokestop | true
+| pokestop_id | Nearby Pokestop ID | 9382498723849792348798234.16
+| pokestop_name | Name of nearby Pokestop | The Amazing Pokestop
+| pokestop_url | Image url of nearby Pokestop | https://google.com/imgs/gym.png
+| guild_name | Name of Guild | Test Guild
+| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/dts/pokestops.md b/docs/dts/pokestops.md
new file mode 100644
index 00000000..8d70d5f7
--- /dev/null
+++ b/docs/dts/pokestops.md
@@ -0,0 +1,36 @@
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for Pokestop Lures.
+
+### Pokestops (Lures)
+
+| Place Holder | Description | Example
+|---|---|---|
+| has_lure | Returns if Pokestop has active lure module deployed | true
+| lure_type | Pokestop lure module type | Glacial
+| lure_expire_time | Time lure module will expire | 07:33:19 PM
+| lure_expire_time_24h | Time lure module will expire (24-hour format) | 19:33:19
+| lure_expire_time_left | Time left until lure module expires | 13m, 2s
+| power_up_level | Pokestop power level | 1
+| power_up_points | Pokestop's total power level points | 100
+| power_up_end_time | Pokestop's power up end time | 10:15:09 PM
+| power_up_end_time_24h | Pokestop's power up end time (24-hour format) | 13:28:30
+| power_up_end_time_left | Pokestop's power up time left until expires | 14m, 10s
+| geofence | Geofence name raid boss is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of Pokemon location | 5.980921321
+| lng | Longitude coordinate of Pokemon location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| pokestop_id | Pokestop ID | 9382498723849792348798234.16
+| pokestop_name | Name of Pokestop | The Amazing Pokestop
+| pokestop_url | Image url of Gym | https://google.com/imgs/gym.png
+| lure_img_url | Image url of lure icon | https://google.com/imgs/lure_501.png
+| guild_name | Name of Guild | Test Guild
+| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
+| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/user-guide/dts/quests.md b/docs/dts/quests.md
similarity index 93%
rename from docs/user-guide/dts/quests.md
rename to docs/dts/quests.md
index 6fca5387..31e16445 100644
--- a/docs/user-guide/dts/quests.md
+++ b/docs/dts/quests.md
@@ -1,32 +1,35 @@
-# Dynamic Text Replacement
-
-Use any of the following in your alerts file to structure how notifications will look for field research quests.
-
-### Field Research Quests
-
-| Place Holder | Description | Example
-|---|---|---|
-| quest_task | Quest task message | Catch 5 Pokemon
-| quest_conditions | Quest task conditions | Dark
-| quest_reward | Quest task reward | Chansey
-| quest_reward_img_url | Quest reward image url | http://map.example.com/images/quest.png
-| has_quest_conditions | Returns if the quest has conditions | true
-| is_ditto | Checks if Ditto | true
-| is_shiny | Checks if reward is shiny | false
-| geofence | Geofence name raid boss is in | City1
-| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
-| lat | Latitude coordinate of Pokemon location | 5.980921321
-| lng | Longitude coordinate of Pokemon location | 3.109283009
-| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
-| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
-| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
-| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
-| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
-| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
-| pokestop_id | Pokestop ID | 9382498723849792348798234.16
-| pokestop_name | Name of Pokestop | The Amazing Pokestop
-| pokestop_url | Image url of Gym | https://google.com/imgs/gym.png
-| guild_name | Name of Guild | Test Guild
-| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
-| date_time | Current date and time | 12/12/2020 12:12:12 PM
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for field research quests.
+
+### Field Research Quests
+
+| Place Holder | Description | Example
+|---|---|---|
+| quest_task | Quest task message | Catch 5 Pokemon
+| quest_conditions | Quest task conditions | Dark
+| quest_reward | Quest task reward | Chansey
+| quest_reward_img_url | Quest reward image url | http://map.example.com/images/quest.png
+| has_quest_conditions | Returns if the quest has conditions | true
+| title | |
+| is_ditto | Checks if Ditto | true
+| is_shiny | Checks if reward is shiny | false
+| is_ar | | true
+| with_ar | | false
+| geofence | Geofence name raid boss is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of Pokemon location | 5.980921321
+| lng | Longitude coordinate of Pokemon location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| pokestop_id | Pokestop ID | 9382498723849792348798234.16
+| pokestop_name | Name of Pokestop | The Amazing Pokestop
+| pokestop_url | Image url of Gym | https://google.com/imgs/gym.png
+| guild_name | Name of Guild | Test Guild
+| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/user-guide/dts/raids.md b/docs/dts/raids.md
similarity index 83%
rename from docs/user-guide/dts/raids.md
rename to docs/dts/raids.md
index 169d79e7..7e594287 100644
--- a/docs/user-guide/dts/raids.md
+++ b/docs/dts/raids.md
@@ -1,65 +1,70 @@
-# Dynamic Text Replacement
-
-Use any of the following in your alerts file to structure how notifications will look for Raids and Eggs.
-
-### Raids & Eggs
-
-| Place Holder | Description | Example
-|---|---|---|
-| pkmn_id | Raid boss pokedex ID | 1
-| pkmn_id_3 | Raid boss pokedex ID (always 3 digits) | 001
-| pkmn_name | Raid boss pokemon name | Bulbasaur
-| pkmn_img_url | Raid boss pokemon image url | http://example.com/your-specified-pokemon-url
-| form | Pokemon form name | Alolan
-| form_id | Form ID | 65
-| form_id_3 | Form ID (always 3 digits) | 065
-| costume | Pokemon costume name | Holiday
-| costume_id | Costume ID | 10
-| costume_id_3 | Costume ID (always 3 digits) | 010
-| is_egg | Returns if raid is egg and not hatched | false
-| is_ex | Returns if raid is ex pass eligible | true
-| ex_emoji | Ex emoji icon | Ex
-| team | Team name that has gym control | Valor
-| team_emoji | Emoji of team that has gym control | <:valor:930824>
-| cp | Raid boss combat power value | 36150
-| lvl | Raid boss level | 5
-| gender | Pokemon gender | Gender icon
-| move_1 | Fast move name | Quick Attack
-| move_2 | Charge move name | Thunder
-| moveset | Fast & Charge move names | Quick Attack/Thunder
-| type_1 | Pokemon type | Dark
-| type_2 | Pokemon type | Water
-| type_1_emoji | Pokemon type emoji | <:00000:types_water>
-| type_2_emoji | Pokemon type emoji | <:00000:types_rock>
-| types | Both types (if 2nd exists) | Dark/Fire
-| types_emoji | Type Discord emoji | <:00000:types_fire> <00001:types_dark>
-| weaknesses | Raid boss weaknesses | Rock, Ground, Dark
-| weaknesses_emoji | Emoji(s) of raid boss weaknesses | Rock Ground Dark
-| perfect_cp | Perfect IV CP | 1831
-| perfect_cp_boosted | Perfect IV CP if Weather boosted | 2351
-| worst_cp | Worst IV CP | 1530
-| worst_cp_boosted | Worst IV CP if Weather boosted | 1339
-| start_time | Raid start time | 08:32:00 AM
-| start_time_24h | Raid start time (24-hour format) | 08:32:00
-| start_time_left | Time left until raid starts | 43m, 33s
-| end_time | Raid end time | 09:15:10 AM
-| end_time_24h | Raid end time (24-hour format) | 09:15:10
-| end_time_left | Time left until raid ends | 45, 11s
-| time_left | Minutes and seconds of time left until despawn | 29m, 30s
-| geofence | Geofence name raid boss is in | City1
-| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
-| lat | Latitude coordinate of Pokemon location | 5.980921321
-| lng | Longitude coordinate of Pokemon location | 3.109283009
-| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
-| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
-| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
-| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
-| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
-| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
-| gym_id | Gym ID | 9382498723849792348798234.16
-| gym_name | Name of Gym | The Amazing Gym
-| gym_url | Image url of Gym | https://google.com/imgs/gym.png
-| guild_name | Name of Guild | Test Guild
-| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
-| date_time | Current date and time | 12/12/2020 12:12:12 PM
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for Raids and Eggs.
+
+### Raids & Eggs
+
+| Place Holder | Description | Example
+|---|---|---|
+| pkmn_id | Raid boss pokedex ID | 1
+| pkmn_id_3 | Raid boss pokedex ID (always 3 digits) | 001
+| pkmn_name | Raid boss pokemon name | Bulbasaur
+| pkmn_img_url | Raid boss pokemon image url | http://example.com/your-specified-pokemon-url
+| form | Pokemon form name | Alolan
+| form_id | Form ID | 65
+| form_id_3 | Form ID (always 3 digits) | 065
+| costume | Pokemon costume name | Holiday
+| costume_id | Costume ID | 10
+| costume_id_3 | Costume ID (always 3 digits) | 010
+| is_egg | Returns if raid is egg and not hatched | false
+| is_ex | Returns if raid is ex pass eligible | true
+| ex_emoji | Ex emoji icon | Ex
+| team | Team name that has gym control | Valor
+| team_emoji | Emoji of team that has gym control | <:valor:930824>
+| cp | Raid boss combat power value | 36150
+| lvl | Raid boss level | 5
+| gender | Pokemon gender | Gender icon
+| move_1 | Fast move name | Quick Attack
+| move_2 | Charge move name | Thunder
+| moveset | Fast & Charge move names | Quick Attack/Thunder
+| type_1 | Pokemon type | Dark
+| type_2 | Pokemon type | Water
+| types | Both Pokemon types (if 2nd exists) | Dark/Fire
+| types_emoji | Type Discord emoji | <:00000:types_fire> <00001:types_dark>
+| weaknesses | Raid boss weaknesses | Rock, Ground, Dark
+| weaknesses_emoji | Emoji(s) of raid boss weaknesses | Rock Ground Dark
+| perfect_cp | Perfect IV CP | 1831
+| perfect_cp_boosted | Perfect IV CP if Weather boosted | 2351
+| worst_cp | Worst IV CP | 1530
+| worst_cp_boosted | Worst IV CP if Weather boosted | 1339
+| start_time | Raid start time | 08:32:00 AM
+| start_time_24h | Raid start time (24-hour format) | 08:32:00
+| start_time_left | Time left until raid starts | 43m, 33s
+| end_time | Raid end time | 09:15:10 AM
+| end_time_24h | Raid end time (24-hour format) | 09:15:10
+| end_time_left | Time left until raid ends | 45m, 11s
+| time_left | Minutes and seconds of time left until despawn | 29m, 30s
+| sponsor_id | Some ID value I have no idea about | 33333
+| partner_id | Some ID value I have no idea about | 44444
+| power_up_level | Gym power level | 1
+| power_up_points | Gym's total power level points | 100
+| power_up_end_time | Gym's power up end time | 10:15:09 PM
+| power_up_end_time_24h | Gym's power up end time (24-hour format) | 13:28:30
+| power_up_end_time_left | Gym's power up time left until expires | 14m, 10s
+| geofence | Geofence name raid boss is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of Pokemon location | 5.980921321
+| lng | Longitude coordinate of Pokemon location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| gym_id | Gym ID | 9382498723849792348798234.16
+| gym_name | Name of Gym | The Amazing Gym
+| gym_url | Image url of Gym | https://google.com/imgs/gym.png
+| guild_name | Name of Guild | Test Guild
+| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/user-guide/dts/weather.md b/docs/dts/weather.md
similarity index 94%
rename from docs/user-guide/dts/weather.md
rename to docs/dts/weather.md
index 614c9bbe..f6bb9e03 100644
--- a/docs/user-guide/dts/weather.md
+++ b/docs/dts/weather.md
@@ -1,36 +1,36 @@
-# Dynamic Text Replacement
-
-Use any of the following in your alerts file to structure how notifications will look for field research quests.
-
-### S2Cell Weather
-
-| Place Holder | Description | Example
-|---|---|---|
-| id | S2Cell weather id | -9938028402
-| weather_condition | In-game gameplay condition | Cloudy
-| has_weather | Returns if there is weather set | true
-| weather | In-game gameplay condition | Cloudy
-| weather_img_url | Weather type image url | http://google.com/imgs/weather_1.png
-| wind_direction | Wind blowing direction | true
-| wind_level | Wind level | 285
-| rain_level | Raid level | 285
-| cloud_level | Cloud level | 285
-| fog_level | Fog level | 285
-| snow_level | Snow level | 285
-| warn_weather | Warning weather | true
-| special_effect_level | Special effect level | 2
-| severity | Weather severity | None/Moderate/Extreme
-| geofence | Geofence name weather cell is in | City1
-| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
-| lat | Latitude coordinate of S2Cell weather location | 5.980921321
-| lng | Longitude coordinate of S2Cell weather location | 3.109283009
-| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
-| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
-| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
-| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
-| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
-| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
-| guild_name | Name of Guild | Test Guild
-| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
-| date_time | Current date and time | 12/12/2020 12:12:12 PM
+# Dynamic Text Replacement
+
+Use any of the following in your embeds file to structure how notifications will look for field research quests.
+
+### S2Cell Weather
+
+| Place Holder | Description | Example
+|---|---|---|
+| id | S2Cell weather id | -9938028402
+| weather_condition | In-game gameplay condition | Cloudy
+| has_weather | Returns if there is weather set | true
+| weather | In-game gameplay condition | Cloudy
+| weather_img_url | Weather type image url | http://google.com/imgs/weather_1.png
+| wind_direction | Wind blowing direction | true
+| wind_level | Wind level | 285
+| rain_level | Raid level | 285
+| cloud_level | Cloud level | 285
+| fog_level | Fog level | 285
+| snow_level | Snow level | 285
+| warn_weather | Warning weather | true
+| special_effect_level | Special effect level | 2
+| severity | Weather severity | None/Moderate/Extreme
+| geofence | Geofence name weather cell is in | City1
+| address | Google Maps or OSM Nominatim address from geocoordinates | 123 Fake St
+| lat | Latitude coordinate of S2Cell weather location | 5.980921321
+| lng | Longitude coordinate of S2Cell weather location | 3.109283009
+| lat_5 | Latitude coordinate shortend to 5th precision | 5.98092
+| lng_5 | Longitude coordinate shortend to 5th precision | 3.10928
+| tilemaps_url | Static tile map url | http://tiles.example.com/static/pokemon-1.png
+| gmaps_url | Google maps location url | https://maps.google.com/maps?q=5.980921321,3.109283009
+| applemaps_url | Apple maps location url | https://maps.apple.com/maps?daddr=5.980921321,3.109283009
+| wazemaps_url | Waze maps location url | https://www.waze.com/ul?ll=5.980921321,3.109283009&navigate=yes
+| guild_name | Name of Guild | Test Guild
+| guild_img_url | Icon image url of Guild | https://discordapp.com/image1.png
+| date_time | Current date and time | 12/12/2020 12:12:12 PM
| br | Newline break | `\r\n`
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
index 90767dfe..a3adcc6d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,65 +1,133 @@
+[](https://github.com/versx/WhMgr/actions)
+[](https://whmgr.rtfd.io)
+[](https://github.com/versx/WhMgr/releases/)
+[](https://github.com/versx/WhMgr/graphs/contributors/)
+[](https://discord.gg/zZ9h9Xa)
+
# Welcome to Webhook Manager
-Works with the following backends:
+**PokeAlarm, PoracleJS, WDR, Novabot, etc alternative.**
+Works with the following backends:
- [RealDeviceMap](https://github.com/123FLO321/RealDeviceMap)
- [Chuck](https://github.com/WatWowMap/Chuck)
- [ChuckDeviceController](https://github.com/versx/ChuckDeviceController)
-Made in C#, runs on .NET Core CLR. Cross platform compatibility, can run on Windows, macOS, and Linux operating systems.
-Sends Discord notifications based on pre-defined filters for Pokemon, raids, raid eggs, field research quests, Team Rocket invasions, gym team changes, and weather. Also supports Discord user's subscribing to Pokemon, raid, quest, Team Rocket invasion, and Pokestop lure notifications via direct messages.
+### Description
+Developed in C#, runs on .NET 5.0 ASP.NET CoreCLR utilizing EntityFramework Core. Cross platform compatibility with Windows, macOS, and Linux operating systems.
+
+Sends Discord notifications based on pre-defined filters for Pokemon, raids, raid eggs, field research quests, Team Rocket invasions, Pokestop lures, gym team changes, and weather changes. It also supports Discord users subscribing to Pokemon, PvP, raid, quest, gym, Team Rocket invasion, and Pokestop lure notifications via direct messages.
-## Features
+### Features
- Supports multiple Discord servers.
-- Discord channel alarm reports for Pokemon, raids, eggs, quests, lures, invasions, gym team changes, and weather.
-- Per user custom Discord notifications for Pokemon, raids, quests, invasions, and lures.
-- User interface to configure Discord notifications with ease (as well as Discord commands). [WhMgr-UI](https://github.com/versx/WhMgr-UI)
-- Subscription notifications based on pre-defined distance.
-- Customizable alert messages with dynamic text replacement.
+- Discord channel alarm reports for Pokemon, raids, eggs, quests, lures, invasions, gym team changes, and weather changes.
+- Built-in Admin Dashboard to configure and manage configuration files.
+- Webhook and subscription queue system, all outgoing messages are queued and fired off in groups for efficiency.
+- If an outgoing message is rate limited, it is backlogged and awaited the rate limit time then requeued.
+- Per user custom Discord notifications for Pokemon, raids, quests, invasions, lures, and gyms.
+- User interface to configure custom Discord subscription notifications with ease. [WhMgr-UI](https://github.com/versx/WhMgr-UI)
+- Subscription notifications based on pre-defined distance and geofence areas.
+- Customizable alert messages with dynamic text replacement/substitution.
- Support for multiple cities/areas using geofences per server.
-- Daily shiny stats reporting.
-- Automatic quest message purge at midnight.
-- Support for Donors/Supporters only notifications.
-- Direct messages of Pokemon notifications based on city roles assigned.
-- Pokemon and Raid subscription notifications based on specific forms.
-- Custom prefix support as well as mentionable user support for commands.
+- Daily shiny and IV stats reporting.
+- Automatic quest message purge at midnight based on timezone.
+- Support for Subscriber only custom notifications.
+- Pokemon, PvP, and Raid subscription notifications based on specific forms or costumes.
+- Custom prefix support as well as mentionable bot user string for commands.
- Raid subscription notifications for specific gyms.
- Twilio text message alerts for ultra rare Pokemon.
- Custom image support for Discord alarm reports.
- Custom icon style selection for Discord user notifications.
- External emoji server support.
-- Custom static map format support.
-- Support for language translation.
+- Custom static map format support, including pokestop and gym marker placements.
+- Support for language translation per instance (per server planned).
- Multi threaded, low processing consumption.
-- [I.C.O.N.S.](https://github.com/Mygod/pokemon-icon-postprocessor) standard image support.
+- [UIcons](https://github.com/uicons/uicons) standard image support.
- Lots more...
-## Direct Message Notification Filters
-- Pokemon ID
-- Pokemon Form
-- Pokemon IV
-- Pokemon Level
-- List of Pokemon Attack/Defense/Stamina values
-- Pokemon Gender
-- Raid Boss
-- City
-- Gym Name
-- Quest Reward
-- Invasion Grunt Type
-- Pokestop Lure Type
-- Distance (meters)
-
-## Frameworks and Libraries
-- .NET Core v2.1.803
-- DSharpPlus v3.2.3
-- DSharpPlus.CommandsNext v3.2.3
-- DSharpPlus.Interactivity v3.2.3
-- Microsoft.Win32.SystemEvents v4.7.0
-- Newtonsoft.Json v12.0.3
-- ServiceStack.OrmLite.MySql v5.8.0
-- Stripe.net v37.14.0
-- Twilio v5.44.0
-
-
-**[Click here](user-guide/config) to get started!**
\ No newline at end of file
+### Subscription Notification Filters
+
+- Pokemon
+ o Pokemon IDs
+ o Pokemon Forms
+ - Pokemon Minimum CP
+ o Pokemon Minimum IV Percentage
+ o Pokemon Minimum Level
+ o Pokemon Maximum Level
+ o List of Pokemon Attack/Defense/Stamina values
+ o Pokemon Gender
+ o Pokemon Size
+ o Custom Location Distance (meters)
+ o City
+
+- Player vs Player (PvP)
+ o Pokemon IDs
+ o Pokemon Forms
+ o PvP League
+ o Pokemon Gender
+ o Minimum Rank
+ o Minimum Stat Product Percentage
+ o Custom Location Distance (meters)
+ o City
+
+- Raids
+ o Raid Boss IDs
+ o Raid Boss Forms
+ o Is EX Eligible
+ o Custom Location Distance (meters)
+ o City
+
+- Gyms
+ o Gym Name
+ o Raid Boss IDs
+ o Minimum Raid Level
+ o Maximum Raid Level
+ o Is EX Eligible
+ o Custom Location Distance (meters)
+
+- Quests
+ o Pokestop Name
+ o Quest Reward Name
+ o Custom Location Distance (meters)
+ o City
+
+- Invasions
+ o Pokestop Name
+ o Invasion Grunt Type IDs
+ o Invasion Reward Pokemon IDs
+ o Custom Location Distance (meters)
+ o City
+
+- Lures
+ o Pokestop Name
+ o Pokestop Lure Type IDs
+ o Custom Location Distance (meters)
+ o City
+
+### Frameworks and Libraries
+- .NET v5.0.404
+- CommandLineParser v2.8.0
+- DSharpPlus v4.1.0
+- DSharpPlus.CommandsNext v4.1.0
+- DSharpPlus.Interactivity v4.1.0
+- GeoTimeZone v4.1.0
+- Handlebars.Net v2.0.10
+- Handlebars.Net.Helpers v2.2.1
+- Microsoft.EntityFrameworkCore v5.0.13
+- Microsoft.EntityFrameworkCore.Design v5.0.13
+- Microsoft.NET.Test.Sdk v17.0
+- Microsoft.VisualStudio.Azure.Containers.Tools.Targets v1.14.0
+- NetTopologySuite v2.4.0
+- NetTopologySuite.Features v2.1.0
+- NetTopologySuite.IO.GeoJSON v2.0.4
+- NUnit v3.13.2
+- NUnit3TestAdapter v4.2.1
+- POGOProtos.Core v2.55.7
+- Pomelo.EntityFrameworkCore.MySql v5.0.3
+- Swashbuckle.AspNetCore v6.2.3
+- System.Runtime.Caching v6.0
+- TimeZoneConverter v5.0
+
+
+**[Click here](install/getting-started.md) to get started!**
\ No newline at end of file
diff --git a/docs/install/docker.md b/docs/install/docker.md
new file mode 100644
index 00000000..c558c95f
--- /dev/null
+++ b/docs/install/docker.md
@@ -0,0 +1 @@
+# Docker Installation
diff --git a/docs/install/getting-started.md b/docs/install/getting-started.md
new file mode 100644
index 00000000..b81adf38
--- /dev/null
+++ b/docs/install/getting-started.md
@@ -0,0 +1,67 @@
+# Getting Started
+
+### __Installation__
+- [Prerequisites](./prerequisites.md)
+- [Install via Docker](./docker.md)
+
+### __Configuration__
+1. Edit `bin/configs/config.json` either open in Notepad/++ or `vi bin/configs/config.json`. [Config Instructions](../config/config.md)
+
+ - [Create bot token](https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token)
+ - Input your bot token and config options.
+ - Fill out the rest of the config options.
+
+1. Edit `bin/alarms/alarms.json` either open in Notepad/++ or `vi bin/alarms/alarms.json`.
+
+1. Fill out the alarms file. [Alarm Instructions](../config/alarms.md)
+
+1. Create directory `bin/geofences` if it doesn't already exist.
+
+1. Create/copy geofence files to `geofences` folder. [Geofence Instructions](../config/geofences.md)
+
+1. Add `dotnet` to your environment path if it isn't already (optional):
+```sh
+export PATH=~/.dotnet/:$PATH
+```
+
+### __Running__
+To run via command line arguments [click here](../other/commandline.md).
+
+1. Build executable from root folder:
+```
+dotnet build
+```
+2. Start Webhook Manager:
+```
+dotnet WhMgr.dll
+dotnet WhMgr.dll --config test.json --name test
+```
+3. User Interface for members to create subscriptions from a website. [WhMgr UI](https://github.com/versx/WhMgr-UI)
+4. Optional reverse location lookup with OpenStreetMaps Nominatim or Google Maps, instructions [here](../other/geocoding.md)
+
+
+
+### __Admin Dashboard__
+Webhook Manager comes with a built-in Admin dashboard to configure and manage all config, discord, filter, geofence, etc files.
+Visit the dashboard at https://127.0.0.1:8008/dashboard
+
+
+### __Discord Permissions__
+Discord recently enabled a new feature that requires you to enable the Privileged Gateway Intents options in the [Discord Developers Portal](https://discord.com/developers/applications) to access Discord member lists.
+
+The bot requires the following Discord permissions:
+
+- Read Messages
+- Send Messages
+- Manage Messages (Prune quest channels)
+- Manage Roles (If cities are enabled)
+- Manage Emojis
+- Embed Links
+- Attach Files (`export` command)
+- Use External Emojis
+
+
+### __Notes__
+- If `dotnet` is not in your path, you'll need to use `~/.dotnet/dotnet` instead of just `dotnet` for commands.
+- If you ran the original install command as `root`, `dotnet` will be located at `/root/.dotnet/dotnet` and you'll need to either use that for build commands or replace the `~/.dotnet/dotnet` path with it when adding to your path.
+- Upon starting, database tables will be automatically created if `subscriptions.enabled` is set to `true`. Emoji icons are also created in the specified `EmojiGuildId` upon connecting to Discord.
diff --git a/docs/install/migrate-v4-to-v5.md b/docs/install/migrate-v4-to-v5.md
new file mode 100644
index 00000000..76a0a229
--- /dev/null
+++ b/docs/install/migrate-v4-to-v5.md
@@ -0,0 +1,17 @@
+# Migrating v4 to v5
+
+- Copy folders from existing v4 instances `bin` folder to v5 `bin` folder.
+- Move config files to `bin/configs` folder.
+- Rename `bin/alerts` to `bin/embeds`
+- Update existing configs with new format.
+- Update existing Discord server configs with new format.
+- Run the following to fix renaming of properties for filters and alarms.
+```
+sed -i 's/alerts/embeds/g' alarms/*.json
+sed -i 's/onlyEx/only_ex/g' filters/*.json
+sed -i 's/ignoreMissing/ignore_missing/g' filters/*.json
+sed -i 's/isShiny/is_shiny/g' filters/*.json
+```
+- Run the `migrate-v4-to-v5.sql` database migration script on your v4 database.
+
+TODO: Expand on more (config migration, filter migration, etc)
\ No newline at end of file
diff --git a/docs/install/prerequisites.md b/docs/install/prerequisites.md
new file mode 100644
index 00000000..1be45ddc
--- /dev/null
+++ b/docs/install/prerequisites.md
@@ -0,0 +1,33 @@
+# Prerequisites
+- Git
+- .NET 5 SDK
+
+
+#### __Git__
+Install [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
+
+#### __.NET 5 SDK__
+```
+# Download install script
+wget https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh
+
+# Make installer executable
+chmod +x dotnet-install.sh
+
+# Install .NET 5.0 SDK
+./dotnet-install.sh --version 5.0.404
+```
+
+
+
+### __Automated Install Scripts__
+Run the following to install .NET 5 software development kit (SDK), clone respository, and copy default example embeds, filters, geofences, config and alarm files.
+
+__Linux/macOS__
+```
+wget https://raw.githubusercontent.com/versx/WhMgr/v5-rewrite/scripts/install.sh && chmod +x install.sh && ./install.sh && rm install.sh
+```
+__Windows__
+```
+bitsadmin /transfer dotnet-install-job /download /priority FOREGROUND https://raw.githubusercontent.com/versx/WhMgr/v5-rewrite/scripts/install.bat install.bat | start install.bat
+```
\ No newline at end of file
diff --git a/docs/updating.md b/docs/install/updating.md
similarity index 59%
rename from docs/updating.md
rename to docs/install/updating.md
index daeb62af..91b2b26b 100644
--- a/docs/updating.md
+++ b/docs/install/updating.md
@@ -1,26 +1,20 @@
-# Updating
-The update scripts will pull latest repository changes, build latest WhMgr.dll, and copy latest locale translation and master files.
-If you'd like to copy any of the latest example files (alerts, filters, templates, geofences) you can provide a parameter when running the script to include them.
-
-**All update commands will do at least the following:**
-- Pull latest repository changes
-- Build latest WhMgr binary/executable
-- Copy latest locale translation files to build folder
-- Copy masterfile.json and cpMultipliers.json files to build folder
-
-
-
-## Update (Normal)
-```
-update.sh
-```
-
-## Update (Copy example filter and alert files)
-```
-update.sh examples
-```
-
-## Update (Copy geofences from root folder)
-```
-update.sh geofences
+# Updating
+The update scripts will pull latest repository changes, build latest WhMgr.dll, and copy latest locale translation and master files.
+If you'd like to copy any of the latest example files (embeds, filters, templates, geofences) you can provide a parameter when running the script to include them.
+
+**All update commands will do at least the following:**
+- Pull latest repository changes
+- Build latest WhMgr binary/executable
+- Copy masterfile.json and cpMultipliers.json files to build folder
+
+
+
+### Update (Normal)
+```
+./scripts/update.sh
+```
+
+### Update (Copy example filter and embed files)
+```
+./scripts/update.sh examples
```
\ No newline at end of file
diff --git a/docs/other/commandline.md b/docs/other/commandline.md
index ca14ad78..9b0b02cc 100644
--- a/docs/other/commandline.md
+++ b/docs/other/commandline.md
@@ -2,8 +2,8 @@
Instead of cloning multiple times in order to run multiple instances you can simply just start it and pass in the specific config to load with.
-`name` - Used to prefix the log file name for the instance
-`config` - Specify a config to load for the instance
+`name` - Used to prefix the log file name for the instance. (optional)
+`config` - Specify a config to load for the instance.
Example:
`dotnet WhMgr.dll --name Test --config test.json`
\ No newline at end of file
diff --git a/docs/other/dashboard.md b/docs/other/dashboard.md
new file mode 100644
index 00000000..9d9ef2e1
--- /dev/null
+++ b/docs/other/dashboard.md
@@ -0,0 +1,80 @@
+# Admin Dashboard
+
+Used to configure and manage all configuration files needed to run Webhook Manager.
+
+Access the dashboard by visiting http://127.0.0.1:8008/dashboard
+
+## Installation
+
+From the root of the project folder run the following commands:
+
+- Copy the Admin Dashboard folder to the `bin` folder
+```cp -R src/ClientApp bin/```
+- Change directories to Admin Dashboard folder
+```cd bin/ClientApp```
+- Install packages and dependencies
+```npm install```
+- Build the Admin Dashboard
+```npm build```
+- Copy the example config file
+```cp src/config.example.json src/config.json```
+- Edit the config file
+```nano src/config.json```
+- Change directories back to the `bin` folder
+```cd ..```
+- Run Webhook Manager
+```dotnet WhMgr.dll``` (or restart via pm2)
+
+
+## Screenshots
+
+### Dashboard
+
+
+### Configs
+
+
+### Edit Config
+
+
+### Discords
+
+
+### Edit Discord
+
+
+### Alarms
+
+
+### Edit Alarm
+
+
+### Filters
+
+
+### Edit Filter
+
+
+### Embeds
+
+
+### New Embed
+
+
+### Edit Embed
+
+
+### Geofences
+
+
+### Edit Geofence
+
+
+### Export Geofence
+
+
+### Discord Roles
+
+
+### Edit Discord Role
+
\ No newline at end of file
diff --git a/docs/other/defaults.md b/docs/other/defaults.md
new file mode 100644
index 00000000..7395282d
--- /dev/null
+++ b/docs/other/defaults.md
@@ -0,0 +1,175 @@
+# Default Options
+
+Located at `bin/static/data/defaults.json`, it provides default values throughout the application.
+
+```json
+{
+ // Default Pokemon subscription options
+ "min_iv": 0,
+ "max_iv": 100,
+ "min_lvl": 0,
+ "max_lvl": 35,
+ "min_cp": 0,
+ "max_cp": 99999,
+ "pvp": {
+ // Default PVP filtering values if none provided
+ "little": {
+ "min_rank": 1,
+ "max_rank": 25,
+ "min_percent": 90,
+ "max_percent": 100,
+ "min_league_cp": 450,
+ "max_league_cp": 500,
+ },
+ "great": {
+ "min_rank": 1,
+ "max_rank": 25,
+ "min_percent": 90,
+ "max_percent": 100,
+ "min_league_cp": 1400,
+ "max_league_cp": 1500,
+ },
+ "ultra": {
+ "min_rank": 1,
+ "max_rank": 25,
+ "min_percent": 90,
+ "max_percent": 100,
+ "min_league_cp": 2400,
+ "max_league_cp": 2500,
+ }
+ },
+
+ // Queue options
+ // Maximum queue batch size when sending outgoing messages
+ "max_queue_batch_size": 10,
+ // Maximum queue size before warning
+ "max_queue_size_warning": 50,
+
+ //
+ "all": "All",
+
+ // Emoji schemas
+ "emoji_schema": "<:{0}:{1}>",
+ "type_emoji_schema": "<:types_{0}:{1}>",
+
+ // Pokemon generation ranges
+ "pokemon_generation_ranges": {
+ "1": {
+ "gen": 1,
+ "start": 1,
+ "end": 151
+ },
+ "2": {
+ "gen": 2,
+ "start": 152,
+ "end": 251
+ },
+ "3": {
+ "gen": 3,
+ "start": 252,
+ "end": 385
+ },
+ "4": {
+ "gen": 4,
+ "start": 386,
+ "end": 493
+ },
+ "5": {
+ "gen": 5,
+ "start": 494,
+ "end": 649
+ },
+ "6": {
+ "gen": 6,
+ "start": 650,
+ "end": 721
+ },
+ "7": {
+ "gen": 7,
+ "start": 722,
+ "end": 809
+ },
+ "8": {
+ "gen": 8,
+ "start": 810,
+ "end": 890
+ }
+ },
+
+ // Default emojis list
+ "emoji_list": [
+ // Teams
+ "neutral",
+ "valor",
+ "mystic",
+ "instinct",
+
+ // Capture rates
+ "capture_1",
+ "capture_2",
+ "capture_3",
+
+ // Weather
+ "weather_1", // Clear
+ "weather_2", // Rain
+ "weather_3", // PartlyCloudy/Overcast
+ "weather_4", // Cloudy
+ "weather_5", // Windy
+ "weather_6", // Snow
+ "weather_7", // Fog
+
+ // Gyms
+ "ar",
+ "ex",
+
+ // Pokemon types
+ "types_fire",
+ "types_grass",
+ "types_ground",
+ "types_rock",
+ "types_water",
+ "types_ghost",
+ "types_ice",
+ "types_dragon",
+ "types_fairy",
+ "types_fighting",
+ "types_bug",
+ "types_psychic",
+ "types_electric",
+ "types_steel",
+ "types_dark",
+ "types_normal",
+ "types_flying",
+ "types_poison",
+
+ // Pvp leagues
+ "league_great",
+ "league_ultra",
+
+ // Pokemon genders
+ "gender_male",
+ "gender_female",
+ "gender_less"
+ ],
+
+ // Weather boost dictionary
+ "weather_boosts": {
+ // None
+ "0": [],
+ // Clear/Sunny
+ "1": ["Fire", "Grass", "Ground"],
+ // Rainy
+ "2": ["Water", "Electric", "Bug"],
+ // Partly Cloudy
+ "3": ["Normal", "Rock"],
+ // Cloudy / Overcast
+ "4": ["Fairy", "Fighting", "Poison"],
+ // Windy
+ "5": ["Dragon", "Flying", "Psychic"],
+ // Snow
+ "6": ["Ice", "Steel"],
+ // Fog
+ "7": ["Dark", "Ghost"]
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/other/geocoding.md b/docs/other/geocoding.md
new file mode 100644
index 00000000..2d0dc67e
--- /dev/null
+++ b/docs/other/geocoding.md
@@ -0,0 +1,166 @@
+# Reverse Geocoding
+
+Convert latitude and longitude coordinates to street addresses.
+
+Supported Providers:
+
+- Google Maps
+- OpenStreetMaps Nominatim
+
+## Google Maps Geocoding
+### __Setup__
+- [Getting started](https://console.cloud.google.com/google/maps-apis/start)
+- [Create an API key](https://developers.google.com/maps/documentation/geocoding/get-api-key)
+
+### __Available DTS Options__
+```json
+{
+ "plus_code": {
+ "compound_code": "2X5Q\u002BXX Yucaipa, CA, USA",
+ "global_code": "85642X5Q\u002BXX"
+ },
+ "results": [
+ {
+ "address_components": [
+ {
+ "long_name": "13403",
+ "short_name": "13403",
+ "types": [
+ "street_number"
+ ]
+ },
+ {
+ "long_name": "Canyon Crest Road",
+ "short_name": "Canyon Crest Rd",
+ "types": [
+ "route"
+ ]
+ },
+ {
+ "long_name": "Yucaipa",
+ "short_name": "Yucaipa",
+ "types": [
+ "locality",
+ "political"
+ ]
+ },
+ {
+ "long_name": "San Bernardino County",
+ "short_name": "San Bernardino County",
+ "types": [
+ "administrative_area_level_2",
+ "political"
+ ]
+ },
+ {
+ "long_name": "California",
+ "short_name": "CA",
+ "types": [
+ "administrative_area_level_1",
+ "political"
+ ]
+ },
+ {
+ "long_name": "United States",
+ "short_name": "US",
+ "types": [
+ "country",
+ "political"
+ ]
+ },
+ {
+ "long_name": "92399",
+ "short_name": "92399",
+ "types": [
+ "postal_code"
+ ]
+ },
+ {
+ "long_name": "5823",
+ "short_name": "5823",
+ "types": [
+ "postal_code_suffix"
+ ]
+ }
+ ],
+ "formatted_address": "13403 Canyon Crest Rd, Yucaipa, CA 92399, USA",
+ "geometry": {
+ "bounds": {
+ "northeast": {
+ "lat": 34.0099215,
+ "lng": -117.0098454
+ },
+ "southwest": {
+ "lat": 34.009714,
+ "lng": -117.010073
+ }
+ },
+ "location": {
+ "lat": 34.0098401,
+ "lng": -117.0099373
+ },
+ "location_type": "ROOFTOP",
+ "viewport": {
+ "northeast": {
+ "lat": 34.01116673029149,
+ "lng": -117.0086102197085
+ },
+ "southwest": {
+ "lat": 34.00846876970849,
+ "lng": -117.0113081802915
+ }
+ }
+ },
+ "place_id": "ChIJGfc7IW1Q24ARogf1hYAtakw",
+ "types": [
+ "premise"
+ ]
+ }
+ ],
+ "status": "OK"
+}
+```
+
+
+## OpenStreetMaps Nominatim
+### __Setup__
+- [Self Hosting](https://nominatim.org/release-docs/latest/admin/Installation/)
+- [Testing Endpoint](https://nominatim.openstreetmap.org) (never use in production)
+
+### __Available DTS Options__
+```json
+{
+ "place_id": 265892028,
+ "licence": "Data \u00A9 OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
+ "osm_type": "way",
+ "osm_id": 30602906,
+ "lat": "34.010038",
+ "lon": "-117.010446",
+ "place_rank": 30,
+ "category": "place",
+ "type": "house",
+ "importance": -1.15,
+ "addresstype": "place",
+ "name": null,
+ "display_name": "36398, Canyon Terrace Drive, Yucaipa, San Bernardino County, California, 92399, United States",
+ "address": {
+ "house_number": "36398",
+ "road": "Canyon Terrace Drive",
+ "neighbourhood": null,
+ "suburb": null,
+ "city": "Yucaipa",
+ "county": "San Bernardino County",
+ "state": "California",
+ "postcode": "92399",
+ "country": "United States",
+ "country_code": "us"
+ },
+ "boundingbox": [
+ "34.009988",
+ "34.010088",
+ "-117.010496",
+ "-117.010396"
+ ]
+}
+
+```
\ No newline at end of file
diff --git a/docs/other/healthchecks.md b/docs/other/healthchecks.md
new file mode 100644
index 00000000..bd24dd0e
--- /dev/null
+++ b/docs/other/healthchecks.md
@@ -0,0 +1,61 @@
+# Health Checks UI & API
+
+Application health checks user interface and API.
+
+**Available Endpoints**:
+
+- http://ip:port/health
+- http://ip:port/health-ui
+
+### Health Checks API Example
+```json
+{
+ "status": "Healthy",
+ "totalDuration": "00:00:00.0283632",
+ "entries": {
+ "Subscriptions Database": {
+ "data": {},
+ "duration": "00:00:00.0082672",
+ "status": "Healthy",
+ "tags": []
+ },
+ "Scanner Database": {
+ "data": {},
+ "duration": "00:00:00.0092902",
+ "status": "Healthy",
+ "tags": []
+ },
+ "Nests Database": {
+ "data": {},
+ "duration": "00:00:00.0077391",
+ "status": "Healthy",
+ "tags": []
+ },
+ "Process": {
+ "data": {},
+ "duration": "00:00:00.0132519",
+ "status": "Healthy",
+ "tags": []
+ },
+ "Allocated Memory": {
+ "data": {},
+ "description": "Allocated megabytes in memory: 160 mb",
+ "duration": "00:00:00.0001172",
+ "status": "Healthy",
+ "tags": []
+ },
+ "Local Disk Storage": {
+ "data": {},
+ "duration": "00:00:00.0010851",
+ "status": "Healthy",
+ "tags": []
+ },
+ "Discord Status": {
+ "data": {},
+ "duration": "00:00:00.0281373",
+ "status": "Healthy",
+ "tags": []
+ }
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/other/pm2.md b/docs/other/pm2.md
new file mode 100644
index 00000000..664142e7
--- /dev/null
+++ b/docs/other/pm2.md
@@ -0,0 +1,37 @@
+# PM2 Configuration
+
+**Install PM2**
+https://pm2.keymetrics.io/docs/usage/quick-start/#installation
+
+**Create PM2 Config**
+Create `ecosystem.config.js` file with below example file. (can be named anything)
+
+**Run**
+Start with `pm2 start ecosystem.config.js`
+
+
+Example PM2 ecosystem configuration file:
+```js
+module.exports = {
+ apps: [
+ {
+ name: "WhMgr1",
+ script: "WhMgr.dll",
+ args: "--name Test --config config.test.json",
+ watch: true,
+ cwd: "/home/user/whmgr/bin",
+ interpreter: "dotnet",
+ max_memory_restart: "2G",
+ autorestart: true,
+ instances: 1,
+ exec_mode: "fork"
+ },
+ {
+ ...
+ }
+ ]
+};
+```
+
+**PM2 Auto Startup Instructions**
+https://pm2.keymetrics.io/docs/usage/startup/
\ No newline at end of file
diff --git a/docs/other/staticmaps.md b/docs/other/staticmaps.md
new file mode 100644
index 00000000..efffc2bd
--- /dev/null
+++ b/docs/other/staticmaps.md
@@ -0,0 +1 @@
+# Static Maps Generator
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 5570e294..d3c01542 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1 +1,2 @@
-mkdocs-bootswatch==1.0
\ No newline at end of file
+mkdocs-bootswatch==1.0
+mkdocs>=1.2.3 # not directly required, pinned by Snyk to avoid a vulnerability
\ No newline at end of file
diff --git a/docs/screenshots.md b/docs/screenshots.md
index 3de4051c..a0935642 100644
--- a/docs/screenshots.md
+++ b/docs/screenshots.md
@@ -1,38 +1,41 @@
# Screenshots
-*All examples are completely customizable using Dynamic Text Replacement/Substitution*
+*All examples are completely customizable using [Dynamic Text Replacement/Substitution](dts/index.md)*
## Pokemon Notifications
-
+
## Pokemon PVP Notifications
-
+
-## Raid Notifications
-
+## Raid Boss Notifications
+
## Raid Egg Notifications
-
+
## Quest Notifications
-
+
## Lure Notifications
-
+
+
+## Rainy Lure Notifications
+ Notifications")
## Glacial Lure Notifications
- Notifications")
+ Notifications")
## Mossy Lure Notifications
- Notifications")
+ Notifications")
## Magnetic Lure Notifications
- Notifications")
+ Notifications")
## Gym Team Takeover Notifications
-
+
## Team Rocket Invasion Notifications
-
+
## Weather Notifications
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/terminology.md b/docs/terminology.md
index 4a70e32f..5177a7bf 100644
--- a/docs/terminology.md
+++ b/docs/terminology.md
@@ -1,5 +1,5 @@
# Terminology
-- **Alerts:** Discord message structures for channel messages or direct message subscriptions
+- **Embeds:** Discord message structures for channel messages or direct message subscriptions
- **Subscriptions:** Custom user defined Pokemon, Raid, Quest, Invasion, or Gym direct messages subscriptions
- **Geofences:** Area restrictions per Alarm
- **Alarms:** Discord channel messages
\ No newline at end of file
diff --git a/docs/user-guide/alerts.md b/docs/user-guide/alerts.md
deleted file mode 100644
index ef748a49..00000000
--- a/docs/user-guide/alerts.md
+++ /dev/null
@@ -1,171 +0,0 @@
-# Alerts
-Alerts depict how Discord embed messages are formatted. Customization is endless.
-
-`` - Replacement placeholders.
-`<#condition>` - Conditional replacements.
-
-**Replacement Placeholders**
-Placeholders are used to build a template (similar to [mustache](https://mustache.github.io/)) which are replaced with real values from incoming webhooks and used to send outgoing Discord messages.
-
-**Conditional replacements**
-Enable the ability to only show something if the conditional value evaluates to `true`. A prime example would be if the Pokemon is near a Pokestop, to include the Pokestop name and image. Below is an example of it:
-```
-<#near_pokestop>**Near Pokestop:** []()
-```
-
-`` - Replaced by the name of the nearby Pokestop.
-`` - Replaced by the image url of the nearby Pokestop.
-`
` - Replaced with a new line break to preserve readability and formatting.
-
-For a list of available dynamic text substitution/replacement options check out the [DTS](user-guide/dts/) pages.
-
-
-## Alert Message Structures
-```js
-{
- "pokemon": {
- "avatarUrl": "",
- "content": "