mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-06 09:22:26 +00:00
Active addresses command (#840)
* feat: added active command * fix: added more examples to onchain * fix: lint issues * fix: linting issues * fix: linting issues
This commit is contained in:
@@ -97,5 +97,8 @@ API_COINBASE_PASS_PHRASE = os.getenv("GT_API_COINBASE_PASS_PHRASE") or "REPLACE_
|
||||
# https://docs.whale-alert.io/
|
||||
API_WHALE_ALERT_KEY = os.getenv("GT_API_WHALE_ALERT_KEY") or "REPLACE_ME"
|
||||
|
||||
# https://docs.glassnode.com/basic-api/api-key#how-to-get-an-api-key
|
||||
API_GLASSNODE_KEY = os.getenv("GT_API_GLASSNODE_KEY") or "REPLACE_ME"
|
||||
|
||||
# https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API
|
||||
API_ETHPLORER_KEY = os.getenv("GT_API_ETHPLORER_KEY") or "freekey"
|
||||
|
||||
@@ -6,6 +6,7 @@ import argparse
|
||||
import os
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
from colorama import Style
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from binance.client import Client
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
@@ -114,8 +115,7 @@ What do you want to do?
|
||||
if self.source != ""
|
||||
else "\nSource: ?\n"
|
||||
)
|
||||
help_text += """
|
||||
Note: Some of CoinGecko commands can fail. Team is working on fix.
|
||||
help_text += f"""
|
||||
|
||||
load load a specific cryptocurrency for analysis
|
||||
chart view a candle chart for a specific cryptocurrency
|
||||
@@ -124,11 +124,12 @@ Note: Some of CoinGecko commands can fail. Team is working on fix.
|
||||
|
||||
> disc discover trending cryptocurrencies, e.g.: top gainers, losers, top sentiment
|
||||
> ov overview of the cryptocurrencies, e.g.: market cap, DeFi, latest news, top exchanges, stables
|
||||
> onchain information on different blockchains, e.g.: eth gas fees, active asset addresses, whale alerts
|
||||
> defi decentralized finance information, e.g.: dpi, llama, tvl, lending, borrow, funding
|
||||
> report generate automatic report {Style.DIM if not self.current_coin else ""}
|
||||
> dd due-diligence for loaded coin, e.g.: coin information, social media, market stats
|
||||
> ta technical analysis for loaded coin, e.g.: ema, macd, rsi, adx, bbands, obv
|
||||
> onchain information on different blockchains, e.g.: eth gas fees
|
||||
> defi decentralized finance information, e.g.: dpi, llama, tvl, lending, borrow, funding
|
||||
> report generate automatic report
|
||||
{Style.RESET_ALL if not self.current_coin else ""}
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ from gamestonk_terminal.cryptocurrency.defi import (
|
||||
substack_view,
|
||||
)
|
||||
|
||||
# pylint: disable=R1732
|
||||
|
||||
|
||||
class DefiController:
|
||||
"""Defi Controller class"""
|
||||
|
||||
49
gamestonk_terminal/cryptocurrency/onchain/glassnode_model.py
Normal file
49
gamestonk_terminal/cryptocurrency/onchain/glassnode_model.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import json
|
||||
import pandas as pd
|
||||
import requests
|
||||
from gamestonk_terminal import config_terminal as cfg
|
||||
|
||||
|
||||
def get_active_addresses(
|
||||
asset: str, interval: str, since: int, until: int
|
||||
) -> pd.DataFrame:
|
||||
"""Returns active addresses of a certain asset
|
||||
[Source: https://glassnode.com]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : str
|
||||
Asset to search active addresses (e.g., BTC)
|
||||
since : int
|
||||
Initial date timestamp (e.g., 1_614_556_800)
|
||||
until : str
|
||||
End date timestamp (e.g., 1_614_556_800)
|
||||
interval : str
|
||||
Interval frequency (e.g., 24h)
|
||||
|
||||
Returns
|
||||
-------
|
||||
pd.DataFrame
|
||||
active addresses over time
|
||||
"""
|
||||
|
||||
url = "https://api.glassnode.com/v1/metrics/addresses/active_count"
|
||||
|
||||
parameters = {
|
||||
"api_key": cfg.API_GLASSNODE_KEY,
|
||||
"a": asset,
|
||||
"i": interval,
|
||||
"s": str(since),
|
||||
"u": str(until),
|
||||
}
|
||||
|
||||
r = requests.get(url, params=parameters)
|
||||
|
||||
if r.status_code == 200:
|
||||
df = pd.DataFrame(json.loads(r.text))
|
||||
df = df.set_index("t")
|
||||
df.index = pd.to_datetime(df.index, unit="s")
|
||||
df = df.loc[df.index > "2010-1-1"]
|
||||
df.reset_index(inplace=True)
|
||||
return df
|
||||
return pd.DataFrame()
|
||||
63
gamestonk_terminal/cryptocurrency/onchain/glassnode_view.py
Normal file
63
gamestonk_terminal/cryptocurrency/onchain/glassnode_view.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import os
|
||||
from matplotlib import pyplot as plt
|
||||
import pandas as pd
|
||||
from gamestonk_terminal.cryptocurrency.onchain.glassnode_model import (
|
||||
get_active_addresses,
|
||||
)
|
||||
from gamestonk_terminal.helper_funcs import export_data, plot_autoscale
|
||||
from gamestonk_terminal import config_plot as cfgPlot
|
||||
|
||||
|
||||
def display_active_addresses(
|
||||
asset: str, since: int, until: int, interval: str, export: str = ""
|
||||
) -> None:
|
||||
"""Display active addresses of a certain asset over time
|
||||
[Source: https://glassnode.org]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : str
|
||||
Asset to search active addresses (e.g., BTC)
|
||||
since : int
|
||||
Initial date timestamp (e.g., 1_614_556_800)
|
||||
until : str
|
||||
End date timestamp (e.g., 1_614_556_800)
|
||||
interval : str
|
||||
Interval frequency (e.g., 24h)
|
||||
export : str
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df_addresses = get_active_addresses(asset, interval, since, until)
|
||||
|
||||
if df_addresses.empty:
|
||||
print("Error in glassnode request")
|
||||
else:
|
||||
plot_data(df_addresses, asset)
|
||||
print("")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"active",
|
||||
df_addresses,
|
||||
)
|
||||
|
||||
|
||||
def plot_data(df, asset):
|
||||
df = df.set_index("t")
|
||||
df.index = pd.to_datetime(df.index, unit="s")
|
||||
df = df.loc[df.index > "2010-1-1"]
|
||||
df.reset_index(inplace=True)
|
||||
|
||||
_, main_ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
|
||||
|
||||
main_ax.plot(df["t"], df["v"], linewidth=0.5)
|
||||
main_ax.set_yscale("log")
|
||||
main_ax.grid(True)
|
||||
|
||||
main_ax.set_title(f"Active {asset} addresses over time")
|
||||
main_ax.set_ylabel("Addresses")
|
||||
main_ax.set_xlabel("Date")
|
||||
|
||||
plt.show()
|
||||
@@ -1,9 +1,12 @@
|
||||
"""Onchain Controller Module"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
# pylint: disable=C0302
|
||||
|
||||
import os
|
||||
import argparse
|
||||
from typing import List
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
@@ -14,16 +17,16 @@ from gamestonk_terminal.helper_funcs import (
|
||||
parse_known_args_and_warn,
|
||||
check_positive,
|
||||
check_int_range,
|
||||
valid_date,
|
||||
)
|
||||
|
||||
from gamestonk_terminal.cryptocurrency.onchain import (
|
||||
ethgasstation_view,
|
||||
glassnode_view,
|
||||
whale_alert_view,
|
||||
ethplorer_view,
|
||||
)
|
||||
|
||||
# pylint: disable=R1732
|
||||
|
||||
|
||||
class OnchainController:
|
||||
"""Onchain Controller class"""
|
||||
@@ -62,6 +65,7 @@ class OnchainController:
|
||||
"th",
|
||||
"prices",
|
||||
"address",
|
||||
"active",
|
||||
]
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
@@ -159,12 +163,218 @@ class OnchainController:
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
def call_active(self, other_args: List[str]):
|
||||
"""Process active command"""
|
||||
|
||||
supported_assets = [
|
||||
"BTC",
|
||||
"ETH",
|
||||
"LTC",
|
||||
"AAVE",
|
||||
"ABT",
|
||||
"AMPL",
|
||||
"ANT",
|
||||
"ARMOR",
|
||||
"BADGER",
|
||||
"BAL",
|
||||
"BAND",
|
||||
"BAT",
|
||||
"BIX",
|
||||
"BNT",
|
||||
"BOND",
|
||||
"BRD",
|
||||
"BUSD",
|
||||
"BZRX",
|
||||
"CELR",
|
||||
"CHSB",
|
||||
"CND",
|
||||
"COMP",
|
||||
"CREAM",
|
||||
"CRO",
|
||||
"CRV",
|
||||
"CVC",
|
||||
"CVP",
|
||||
"DAI",
|
||||
"DDX",
|
||||
"DENT",
|
||||
"DGX",
|
||||
"DHT",
|
||||
"DMG",
|
||||
"DODO",
|
||||
"DOUGH",
|
||||
"DRGN",
|
||||
"ELF",
|
||||
"ENG",
|
||||
"ENJ",
|
||||
"EURS",
|
||||
"FET",
|
||||
"FTT",
|
||||
"FUN",
|
||||
"GNO",
|
||||
"GUSD",
|
||||
"HEGIC",
|
||||
"HOT",
|
||||
"HPT",
|
||||
"HT",
|
||||
"HUSD",
|
||||
"INDEX",
|
||||
"KCS",
|
||||
"LAMB",
|
||||
"LBA",
|
||||
"LDO",
|
||||
"LEO",
|
||||
"LINK",
|
||||
"LOOM",
|
||||
"LRC",
|
||||
"MANA",
|
||||
"MATIC",
|
||||
"MCB",
|
||||
"MCO",
|
||||
"MFT",
|
||||
"MIR",
|
||||
"MKR",
|
||||
"MLN",
|
||||
"MTA",
|
||||
"MTL",
|
||||
"MX",
|
||||
"NDX",
|
||||
"NEXO",
|
||||
"NFTX",
|
||||
"NMR",
|
||||
"Nsure",
|
||||
"OCEAN",
|
||||
"OKB",
|
||||
"OMG",
|
||||
"PAX",
|
||||
"PAY",
|
||||
"PERP",
|
||||
"PICKLE",
|
||||
"PNK",
|
||||
"PNT",
|
||||
"POLY",
|
||||
"POWR",
|
||||
"PPT",
|
||||
"QASH",
|
||||
"QKC",
|
||||
"QNT",
|
||||
"RDN",
|
||||
"REN",
|
||||
"REP",
|
||||
"RLC",
|
||||
"ROOK",
|
||||
"RPL",
|
||||
"RSR",
|
||||
"SAI",
|
||||
"SAN",
|
||||
"SNT",
|
||||
"SNX",
|
||||
"STAKE",
|
||||
"STORJ",
|
||||
"sUSD",
|
||||
"SUSHI",
|
||||
"TEL",
|
||||
"TOP",
|
||||
"UBT",
|
||||
"UMA",
|
||||
"UNI",
|
||||
"USDC",
|
||||
"USDK",
|
||||
"USDT",
|
||||
"UTK",
|
||||
"VERI",
|
||||
"WaBi",
|
||||
"WAX",
|
||||
"WBTC",
|
||||
"WETH",
|
||||
"wNMX",
|
||||
"WTC",
|
||||
"YAM",
|
||||
"YFI",
|
||||
"ZRX",
|
||||
]
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="active",
|
||||
description="""
|
||||
Display active blockchain addresses over time
|
||||
[Source: https://glassnode.org]
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--asset",
|
||||
dest="asset",
|
||||
type=str,
|
||||
help="Asset symbol. Default: BTC",
|
||||
default="BTC",
|
||||
choices=supported_assets,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--interval",
|
||||
dest="interval",
|
||||
type=str,
|
||||
help="Frequency interval. Default: 24h",
|
||||
default="24h",
|
||||
choices=["1h", "24h", "10m", "1w", "1month"],
|
||||
)
|
||||
|
||||
# max_timestamp = int(datetime.timestamp(datetime.now()))
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--since",
|
||||
dest="since",
|
||||
type=valid_date,
|
||||
help="Initial date. Default: 2020-01-01",
|
||||
default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
"--until",
|
||||
dest="until",
|
||||
type=valid_date,
|
||||
help="Final date. Default: 2021-01-01",
|
||||
default=(datetime.now()).strftime("%Y-%m-%d"),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
glassnode_view.display_active_addresses(
|
||||
asset=ns_parser.asset,
|
||||
interval=ns_parser.interval,
|
||||
since=int(datetime.timestamp(ns_parser.since)),
|
||||
until=int(datetime.timestamp(ns_parser.until)),
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def call_whales(self, other_args: List[str]):
|
||||
"""Process whales command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="wales",
|
||||
prog="whales",
|
||||
description="""
|
||||
Display crypto whales transactions.
|
||||
[Source: https://docs.whale-alert.io/]
|
||||
@@ -845,7 +1055,13 @@ Onchain:
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
|
||||
Eth Gas Station:
|
||||
gwei check current eth gas fees
|
||||
|
||||
Glassnode:
|
||||
active check active addresses in a certain blockchain
|
||||
|
||||
Whale Alert:
|
||||
whales check crypto wales transactions
|
||||
"""
|
||||
help_text += f"\nEthereum address: {self.address if self.address else '?'}"
|
||||
@@ -854,7 +1070,7 @@ Onchain:
|
||||
)
|
||||
|
||||
help_text += """
|
||||
Ethereum:
|
||||
Ethereum [Ethplorer]:
|
||||
address load ethereum address of token, account or transaction
|
||||
top top ERC20 tokens"""
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""Papermill Controller Module"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
# pylint: disable=R1732
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from typing import List
|
||||
@@ -14,8 +16,6 @@ from gamestonk_terminal.helper_funcs import get_flair
|
||||
from gamestonk_terminal import config_terminal
|
||||
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn
|
||||
|
||||
# pylint: disable=R1732
|
||||
|
||||
|
||||
class ReportController:
|
||||
"""Report Controller class"""
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""Papermill Controller Module"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
# pylint: disable=R1732
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from typing import List
|
||||
@@ -16,8 +18,6 @@ from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.helper_funcs import get_flair
|
||||
from gamestonk_terminal import config_terminal
|
||||
|
||||
# pylint: disable=R1732
|
||||
|
||||
|
||||
class ReportController:
|
||||
"""Report Controller class"""
|
||||
|
||||
23
website/content/cryptocurrency/onchain/active/_index.md
vendored
Normal file
23
website/content/cryptocurrency/onchain/active/_index.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
```
|
||||
usage: active [-a ASSET]
|
||||
[-s INITIAL_DATE]
|
||||
[-u END_DATE]
|
||||
[-i {1h, 24h, 10m, 1w, 1month}]
|
||||
[--export {csv,json,xlsx}] [-h]
|
||||
```
|
||||
|
||||
Display active addresses on a certain blockchain over time [Source: https://glassnode.org]
|
||||
|
||||
The number of unique addresses that were active either as a sender or receiver. Only addresses that were active in successful transactions are counted.
|
||||
|
||||
Supported assets: BTC, ETH, LTC, AAVE, ABT, AMPL, ANT, ARMOR, BADGER, BAL, BAND, BAT, BIX, BNT, BOND, BRD, BUSD, BZRX, CELR, CHSB, CND, COMP, CREAM, CRO, CRV, CVC, CVP, DAI, DDX, DENT, DGX, DHT, DMG, DODO, DOUGH, DRGN, ELF, ENG, ENJ, EURS, FET, FTT, FUN, GNO, GUSD, HEGIC, HOT, HPT, HT, HUSD, INDEX, KCS, LAMB, LBA, LDO, LEO, LINK, LOOM, LRC, MANA, MATIC, MCB, MCO, MFT, MIR, MKR, MLN, MTA, MTL, MX, NDX, NEXO, NFTX, NMR, Nsure, OCEAN, OKB, OMG, PAX, PAY, PERP, PICKLE, PNK, PNT, POLY, POWR, PPT, QASH, QKC, QNT, RDN, REN, REP, RLC, ROOK, RPL, RSR, SAI, SAN, SNT, SNX, STAKE, STORJ, sUSD, SUSHI, TEL, TOP, UBT, UMA, UNI, USDC, USDK, USDT, UTK, VERI, WaBi, WAX, WBTC, WETH, wNMX, WTC, YAM, YFI, ZRX
|
||||
|
||||
```
|
||||
optional arguments:
|
||||
-a ASSET Asset to search for active addresses (default: BTC)
|
||||
-s DATE --since DATE Start date (default: 1 year before, e.g., 2020-10-22)
|
||||
-u DATE --until DATE End date (default: current day, e.g., 2021-10-22)
|
||||
-i INTERV --interval INTERV Interval frequency (default: 24h)
|
||||
--export {csv,json,xlsx} Export dataframe data to csv,json,xlsx file (default: )
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
@@ -6,13 +6,12 @@ usage: whales [-m MIN] [-t TOP]
|
||||
|
||||
Display huge value transactions from major blockchains. [Source: https://docs.whale-alert.io/]
|
||||
|
||||
|
||||
```
|
||||
optional arguments:
|
||||
-m MIN, --min MIN Minimum value of transactions. (default: 1000000)
|
||||
-t TOP, --top TOP top N number of records (default: 10)
|
||||
-s {date,symbol,blockchain,amount,amount_usd,from,to}, --sort {date,symbol,blockchain,amount,amount_usd,from,to}
|
||||
Sort by given column. Default: date (default: date)
|
||||
Sort by given column (default: date)
|
||||
--descend Flag to sort in descending order (lowest first)
|
||||
(default: True)
|
||||
-a, --address Flag to show addresses of transactions (default: False)
|
||||
|
||||
54
website/data/menu/main.yml
vendored
54
website/data/menu/main.yml
vendored
@@ -663,32 +663,32 @@ main:
|
||||
- name: portfolio optimization
|
||||
ref: "/portfolio/portfolio_optimization"
|
||||
sub:
|
||||
- name: select
|
||||
ref: "/portfolio/portfolio_optimization/select"
|
||||
- name: add
|
||||
ref: "/portfolio/portfolio_optimization/add"
|
||||
- name: rmv
|
||||
ref: "/portfolio/portfolio_optimization/rmv"
|
||||
- name: equal
|
||||
ref: "/portfolio/portfolio_optimization/equal"
|
||||
- name: mktcap
|
||||
ref: "/portfolio/portfolio_optimization/mktcap"
|
||||
- name: dividend
|
||||
ref: "/portfolio/portfolio_optimization/dividend"
|
||||
- name: property
|
||||
ref: "/portfolio/portfolio_optimization/property"
|
||||
- name: maxhsarpe
|
||||
ref: "/portfolio/portfolio_optimization/maxsharpe"
|
||||
- name: minvol
|
||||
ref: "/portfolio/portfolio_optimization/minvol"
|
||||
- name: maxquadutil
|
||||
ref: "/portfolio/portfolio_optimization/maxquadutil"
|
||||
- name: effret
|
||||
ref: "/portfolio/portfolio_optimization/effret"
|
||||
- name: effrisk
|
||||
ref: "/portfolio/portfolio_optimization/effrisk"
|
||||
- name: ef
|
||||
ref: "/portfolio/portfolio_optimization/ef"
|
||||
- name: select
|
||||
ref: "/portfolio/portfolio_optimization/select"
|
||||
- name: add
|
||||
ref: "/portfolio/portfolio_optimization/add"
|
||||
- name: rmv
|
||||
ref: "/portfolio/portfolio_optimization/rmv"
|
||||
- name: equal
|
||||
ref: "/portfolio/portfolio_optimization/equal"
|
||||
- name: mktcap
|
||||
ref: "/portfolio/portfolio_optimization/mktcap"
|
||||
- name: dividend
|
||||
ref: "/portfolio/portfolio_optimization/dividend"
|
||||
- name: property
|
||||
ref: "/portfolio/portfolio_optimization/property"
|
||||
- name: maxhsarpe
|
||||
ref: "/portfolio/portfolio_optimization/maxsharpe"
|
||||
- name: minvol
|
||||
ref: "/portfolio/portfolio_optimization/minvol"
|
||||
- name: maxquadutil
|
||||
ref: "/portfolio/portfolio_optimization/maxquadutil"
|
||||
- name: effret
|
||||
ref: "/portfolio/portfolio_optimization/effret"
|
||||
- name: effrisk
|
||||
ref: "/portfolio/portfolio_optimization/effrisk"
|
||||
- name: ef
|
||||
ref: "/portfolio/portfolio_optimization/ef"
|
||||
- name: cryptocurrency
|
||||
ref: "/cryptocurrency"
|
||||
sub:
|
||||
@@ -830,6 +830,8 @@ main:
|
||||
sub:
|
||||
- name: gwei
|
||||
ref: "/cryptocurrency/onchain/gwei"
|
||||
- name: active
|
||||
ref: "/cryptocurrency/onchain/active"
|
||||
- name: technical analysis
|
||||
ref: "/cryptocurrency/technical_analysis"
|
||||
sub:
|
||||
|
||||
Reference in New Issue
Block a user