The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .all-contributorsrc
├── .codeclimate.yml
├── .deepsource.toml
├── .editorconfig
├── .gitattributes
├── .github
    ├── FUNDING.yml
    ├── ISSUE_TEMPLATE
    │   ├── Bug.md
    │   ├── Feature.md
    │   └── Support.md
    ├── PULL_REQUEST_TEMPLATE.md
    ├── PULL_REQUEST_TEMPLATE
    │   ├── Improvement.md
    │   └── Other.md
    ├── dependabot.yml
    ├── stale.yml
    └── workflows
    │   ├── codeql-analysis.yml
    │   ├── golangci-lint.yml
    │   ├── goreleaser.yml
    │   ├── pr-checks.yml
    │   └── staticcheck.yml
├── .gitignore
├── .gitmodules
├── .golangci.yml
├── .goreleaser.yml
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── Dockerfile.build
├── LICENSE.md
├── Makefile
├── README.md
├── SECURITY.md
├── _sample_configs
    ├── bargraph_config.png
    ├── bargraph_config.yml
    ├── dynamic_sizing.yml
    ├── kubernetes_config.png
    ├── kubernetes_config.yml
    ├── sample_config.png
    ├── sample_config.yml
    ├── small_config.yml
    ├── uniconfig.png
    └── uniconfig.yml
├── app
    ├── app_manager.go
    ├── display.go
    ├── exit_message.go
    ├── exit_message_test.go
    ├── focus_tracker.go
    ├── module_validator.go
    ├── module_validator_test.go
    ├── scheduler.go
    ├── scheduler_test.go
    ├── widget_maker.go
    ├── widget_maker_test.go
    └── wtf_app.go
├── cfg
    ├── common_settings.go
    ├── common_settings_test.go
    ├── config_files.go
    ├── copy.go
    ├── default_color_theme.go
    ├── default_color_theme_test.go
    ├── default_config_file.go
    ├── error_messages.go
    ├── parsers.go
    ├── parsers_test.go
    ├── position_settings.go
    ├── position_validation.go
    ├── position_validation_test.go
    ├── secrets.go
    ├── validatable.go
    ├── validations.go
    └── validations_test.go
├── checklist
    ├── checklist.go
    ├── checklist_item.go
    ├── checklist_item_test.go
    └── checklist_test.go
├── flags
    └── flags.go
├── generator
    ├── settings.tpl
    ├── textwidget.go
    └── textwidget.tpl
├── go.mod
├── go.sum
├── help
    └── help.go
├── images
    ├── dude_wtf.png
    ├── logo_transparent.png
    ├── screenshot.jpg
    └── sponsors
    │   ├── airbrake.png
    │   ├── robusta.png
    │   ├── warp.png
    │   ├── warp2.png
    │   └── warp3.png
├── logger
    └── log.go
├── main.go
├── modules
    ├── airbrake
    │   ├── client.go
    │   ├── group_info_table.go
    │   ├── keyboard.go
    │   ├── result_table.go
    │   ├── settings.go
    │   ├── util.go
    │   └── widget.go
    ├── asana
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── azuredevops
    │   ├── client.go
    │   ├── example-conf.yml
    │   ├── settings.go
    │   └── widget.go
    ├── bamboohr
    │   ├── calendar.go
    │   ├── client.go
    │   ├── employee.go
    │   ├── item.go
    │   ├── request.go
    │   ├── settings.go
    │   └── widget.go
    ├── bargraph
    │   ├── settings.go
    │   └── widget.go
    ├── buildkite
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── pipelines_display_data.go
    │   ├── settings.go
    │   └── widget.go
    ├── cds
    │   ├── favorites
    │   │   ├── display.go
    │   │   ├── keyboard.go
    │   │   ├── settings.go
    │   │   └── widget.go
    │   ├── queue
    │   │   ├── display.go
    │   │   ├── keyboard.go
    │   │   ├── settings.go
    │   │   └── widget.go
    │   └── status
    │   │   ├── display.go
    │   │   ├── keyboard.go
    │   │   ├── settings.go
    │   │   └── widget.go
    ├── circleci
    │   ├── build.go
    │   ├── client.go
    │   ├── settings.go
    │   └── widget.go
    ├── clocks
    │   ├── clock.go
    │   ├── clock_collection.go
    │   ├── display.go
    │   ├── settings.go
    │   └── widget.go
    ├── cmdrunner
    │   ├── settings.go
    │   └── widget.go
    ├── covid
    │   ├── cases.go
    │   ├── cases_test.go
    │   ├── client.go
    │   ├── settings.go
    │   └── widget.go
    ├── cryptocurrency
    │   ├── bittrex
    │   │   ├── bittrex.go
    │   │   ├── display.go
    │   │   ├── settings.go
    │   │   └── widget.go
    │   ├── blockfolio
    │   │   ├── settings.go
    │   │   └── widget.go
    │   ├── cryptolive
    │   │   ├── price
    │   │   │   ├── price.go
    │   │   │   ├── settings.go
    │   │   │   └── widget.go
    │   │   ├── settings.go
    │   │   ├── toplist
    │   │   │   ├── display.go
    │   │   │   ├── settings.go
    │   │   │   ├── toplist.go
    │   │   │   └── widget.go
    │   │   └── widget.go
    │   └── mempool
    │   │   ├── settings.go
    │   │   └── widget.go
    ├── datadog
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── devto
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── digitalclock
    │   ├── clocks.go
    │   ├── display.go
    │   ├── fonts.go
    │   ├── settings.go
    │   └── widget.go
    ├── digitalocean
    │   ├── display.go
    │   ├── droplet.go
    │   ├── droplet_properties_table.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── docker
    │   ├── client.go
    │   ├── example-conf.yml
    │   ├── settings.go
    │   ├── utils.go
    │   └── widget.go
    ├── feedreader
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── widget.go
    │   └── widget_test.go
    ├── football
    │   ├── client.go
    │   ├── settings.go
    │   ├── types.go
    │   ├── util.go
    │   └── widget.go
    ├── gcal
    │   ├── cal_event.go
    │   ├── client.go
    │   ├── display.go
    │   ├── display_test.go
    │   ├── settings.go
    │   └── widget.go
    ├── gerrit
    │   ├── display.go
    │   ├── gerrit_repo.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── git
    │   ├── display.go
    │   ├── git_repo.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── variables.go
    │   ├── variables_win.go
    │   └── widget.go
    ├── github
    │   ├── display.go
    │   ├── github_repo.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── gitlab
    │   ├── display.go
    │   ├── gitlab_project.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── gitlabtodo
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── gitter
    │   ├── client.go
    │   ├── gitter.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── googleanalytics
    │   ├── client.go
    │   ├── display.go
    │   ├── settings.go
    │   └── widget.go
    ├── grafana
    │   ├── client.go
    │   ├── display.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── gspreadsheets
    │   ├── client.go
    │   ├── settings.go
    │   └── widget.go
    ├── hackernews
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── story.go
    │   ├── story_test.go
    │   └── widget.go
    ├── healthchecks
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── hibp
    │   ├── client.go
    │   ├── hibp_breach.go
    │   ├── hibp_status.go
    │   ├── settings.go
    │   └── widget.go
    ├── ipaddresses
    │   ├── ipapi
    │   │   ├── settings.go
    │   │   └── widget.go
    │   └── ipinfo
    │   │   ├── settings.go
    │   │   └── widget.go
    ├── jenkins
    │   ├── client.go
    │   ├── job.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── view.go
    │   └── widget.go
    ├── jira
    │   ├── client.go
    │   ├── issues.go
    │   ├── keyboard.go
    │   ├── search_result.go
    │   ├── settings.go
    │   └── widget.go
    ├── krisinformation
    │   ├── client.go
    │   ├── settings.go
    │   └── widget.go
    ├── kubernetes
    │   ├── client.go
    │   ├── settings.go
    │   ├── widget.go
    │   └── widget_test.go
    ├── logger
    │   ├── settings.go
    │   └── widget.go
    ├── lunarphase
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── mercurial
    │   ├── display.go
    │   ├── hg_repo.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── nbascore
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── newrelic
    │   ├── client.go
    │   ├── client
    │   │   ├── README.md
    │   │   ├── alert_conditions.go
    │   │   ├── alert_events.go
    │   │   ├── application_deployments.go
    │   │   ├── application_host_metrics.go
    │   │   ├── application_hosts.go
    │   │   ├── application_instance_metrics.go
    │   │   ├── application_instances.go
    │   │   ├── application_metrics.go
    │   │   ├── applications.go
    │   │   ├── array.go
    │   │   ├── browser_applications.go
    │   │   ├── component_metrics.go
    │   │   ├── http_helper.go
    │   │   ├── key_transactions.go
    │   │   ├── legacy_alert_policies.go
    │   │   ├── main.go
    │   │   ├── metrics.go
    │   │   ├── mobile_application_metrics.go
    │   │   ├── mobile_applications.go
    │   │   ├── notification_channels.go
    │   │   ├── server_metrics.go
    │   │   ├── servers.go
    │   │   └── usages.go
    │   ├── display.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── nextbus
    │   ├── settings.go
    │   └── widget.go
    ├── opsgenie
    │   ├── client.go
    │   ├── settings.go
    │   └── widget.go
    ├── pagerduty
    │   ├── client.go
    │   ├── settings.go
    │   ├── sort.go
    │   └── widget.go
    ├── pihole
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── view.go
    │   └── widget.go
    ├── pivotal
    │   ├── client.go
    │   ├── display.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── structs.go
    │   ├── view.go
    │   └── widget.go
    ├── pocket
    │   ├── client.go
    │   ├── item_service.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── power
    │   ├── battery.go
    │   ├── battery_freebsd.go
    │   ├── battery_linux.go
    │   ├── managed_device_test.go
    │   ├── managed_devices.go
    │   ├── settings.go
    │   ├── source.go
    │   ├── source_freebsd.go
    │   ├── source_linux.go
    │   └── widget.go
    ├── progress
    │   ├── settings.go
    │   └── widget.go
    ├── resourceusage
    │   ├── settings.go
    │   └── widget.go
    ├── rollbar
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── rollbar.go
    │   ├── settings.go
    │   └── widget.go
    ├── security
    │   ├── dns.go
    │   ├── firewall.go
    │   ├── security_data.go
    │   ├── settings.go
    │   ├── users.go
    │   ├── widget.go
    │   └── wifi.go
    ├── spacex
    │   ├── client.go
    │   ├── settings.go
    │   └── widget.go
    ├── spotify
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── spotifyweb
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── status
    │   ├── settings.go
    │   └── widget.go
    ├── steam
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── stocks
    │   ├── finnhub
    │   │   ├── client.go
    │   │   ├── quote.go
    │   │   ├── settings.go
    │   │   └── widget.go
    │   └── yfinance
    │   │   ├── settings.go
    │   │   ├── widget.go
    │   │   └── yquote.go
    ├── subreddit
    │   ├── api.go
    │   ├── keyboard.go
    │   ├── link.go
    │   ├── settings.go
    │   └── widget.go
    ├── system
    │   ├── settings.go
    │   ├── system_info.go
    │   ├── system_info_windows.go
    │   └── widget.go
    ├── textfile
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── todo
    │   ├── display.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── todo_plus
    │   ├── backend
    │   │   ├── backend.go
    │   │   ├── project.go
    │   │   ├── todoist.go
    │   │   └── trello.go
    │   ├── display.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── transmission
    │   ├── display.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── travisci
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── travis.go
    │   └── widget.go
    ├── twitch
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── twitter
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── request.go
    │   ├── settings.go
    │   ├── tweet.go
    │   ├── user.go
    │   └── widget.go
    ├── twitterstats
    │   ├── client.go
    │   ├── settings.go
    │   └── widget.go
    ├── unknown
    │   ├── settings.go
    │   └── widget.go
    ├── updown
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── uptimerobot
    │   ├── keyboard.go
    │   ├── settings.go
    │   └── widget.go
    ├── urlcheck
    │   ├── client.go
    │   ├── client_test.go
    │   ├── settings.go
    │   ├── urlResult.go
    │   ├── urlResult_test.go
    │   ├── view.go
    │   └── widget.go
    ├── victorops
    │   ├── client.go
    │   ├── oncallresponse.go
    │   ├── oncallteam.go
    │   ├── settings.go
    │   └── widget.go
    ├── weatherservices
    │   ├── arpansagovau
    │   │   ├── client.go
    │   │   ├── settings.go
    │   │   └── widget.go
    │   ├── prettyweather
    │   │   ├── settings.go
    │   │   └── widget.go
    │   └── weather
    │   │   ├── display.go
    │   │   ├── emoji.go
    │   │   ├── keyboard.go
    │   │   ├── settings.go
    │   │   └── widget.go
    └── zendesk
    │   ├── client.go
    │   ├── keyboard.go
    │   ├── settings.go
    │   ├── tickets.go
    │   └── widget.go
├── package.json
├── scripts
    └── check-uncommitted-vendor-files.sh
├── support
    └── github.go
├── utils
    ├── colors.go
    ├── colors_test.go
    ├── conversions.go
    ├── conversions_test.go
    ├── email_addresses.go
    ├── email_addresses_test.go
    ├── help_parser.go
    ├── homedir.go
    ├── homedir_test.go
    ├── init.go
    ├── init_test.go
    ├── reflective.go
    ├── sums.go
    ├── sums_test.go
    ├── text.go
    ├── text_test.go
    ├── utils.go
    └── utils_test.go
├── view
    ├── bargraph.go
    ├── bargraph_test.go
    ├── base.go
    ├── base_test.go
    ├── billboard_modal.go
    ├── info_table.go
    ├── info_table_test.go
    ├── keyboard_widget.go
    ├── keyboard_widget_test.go
    ├── multisource_widget.go
    ├── scrollable_widget.go
    ├── text_widget.go
    └── text_widget_test.go
└── wtf
    ├── colors.go
    ├── colors_test.go
    ├── datetime.go
    ├── datetime_test.go
    ├── enablable.go
    ├── numbers.go
    ├── numbers_test.go
    ├── schedulable.go
    ├── stoppable.go
    ├── terminal.go
    └── wtfable.go


/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | checks:
3 |   similar-code:
4 |     config:
5 |       threshold: 3
6 | 


--------------------------------------------------------------------------------
/.deepsource.toml:
--------------------------------------------------------------------------------
 1 | version = 1
 2 | 
 3 | [[analyzers]]
 4 | name = "shell"
 5 | enabled = true
 6 | 
 7 | [[analyzers]]
 8 | name = "go"
 9 | enabled = true
10 | 
11 |   [analyzers.meta]
12 |   import_root = "github.com/wtfutil/wtf"
13 | 
14 | [[analyzers]]
15 | name = "test-coverage"
16 | enabled = true


--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
 1 | root = true
 2 | 
 3 | [*]
 4 | end_of_line = lf
 5 | insert_final_newline = true
 6 | max_line_length=120
 7 | 
 8 | [*.go]
 9 | indent_style = tab
10 | indent_size = 4
11 | charset = utf-8
12 | trim_trailing_whitespace = true
13 | 
14 | [*.html]
15 | indent_style = tab
16 | indent_size = 4
17 | charset = utf-8
18 | trim_trailing_whitespace = false
19 | 
20 | [*.md]
21 | trim_trailing_whitespace = false
22 | 


--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | _site/* linguist-vendored
2 | docs/* linguist-vendored
3 | vendor/* linguist-vendored
4 | 


--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: senorprogrammer
2 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name:  🐞 Report a Bug
3 | about: Tell us what's broken
4 | ---
5 | 
6 | ## What's broken?
7 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: ⚡️ Request a Feature
3 | about: Tell us what it should do
4 | ---
5 | 
6 | ## What should it do?
7 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Support.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: ❓Ask a Question
3 | about: Tell us how we can help
4 | ---
5 | 
6 | ## How can we help?
7 | 
8 | 
9 | 


--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | 
2 | Thanks for submitting a pull request. Please provide enough information so that others can review your pull request.
3 | 
4 | 
5 | 


--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/Improvement.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Improvement
3 | about: You have some improvement to make wtf better?
4 | ---
5 | 
6 | Thanks for submitting a pull request. Please provide enough information so that others can review your pull request.
7 | 
8 | 
9 | 


--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/Other.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Other
 3 | about: You have some other ideas you want to introduce?
 4 | ---
 5 | 
 6 | Thanks for submitting a pull request. Please provide enough information so that others can review your pull request.
 7 | 
 8 | 
 9 | 
10 | 


--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
 1 | version: 2
 2 | updates:
 3 | - package-ecosystem: gomod
 4 |   directory: "/"
 5 |   schedule:
 6 |     interval: daily
 7 |   open-pull-requests-limit: 10
 8 |   assignees:
 9 |   - FelicianoTech
10 | - package-ecosystem: "github-actions"
11 |   directory: "/"
12 |   schedule:
13 |     interval: daily
14 |   open-pull-requests-limit: 10
15 |   assignees:
16 |   - FelicianoTech
17 | 


--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
 1 | name: "Code scanning - action"
 2 | 
 3 | on:
 4 |   pull_request:
 5 |   schedule:
 6 |     - cron: '0 3 * * 3,6'
 7 | 
 8 | jobs:
 9 |   CodeQL-Build:
10 | 
11 |     runs-on: ubuntu-latest
12 | 
13 |     steps:
14 |     - name: Checkout repository
15 |       uses: actions/checkout@v4.2.2
16 | 
17 |     # Initializes the CodeQL tools for scanning.
18 |     - name: Initialize CodeQL
19 |       uses: github/codeql-action/init@v3
20 |       # Override language selection by uncommenting this and choosing your languages
21 |       # with:
22 |       #   languages: go, javascript, csharp, python, cpp, java
23 | 
24 |     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
25 |     # If this step fails, then you should remove it and run the build manually (see below)
26 |     - name: Autobuild
27 |       uses: github/codeql-action/autobuild@v3
28 | 
29 |     # ℹ️ Command-line programs to run using the OS shell.
30 |     # 📚 https://git.io/JvXDl
31 | 
32 |     # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
33 |     #    and modify them (or add more) to build your code if your project
34 |     #    uses a compiled language
35 | 
36 |     #- run: |
37 |     #   make bootstrap
38 |     #   make release
39 | 
40 |     - name: Perform CodeQL Analysis
41 |       uses: github/codeql-action/analyze@v3
42 | 


--------------------------------------------------------------------------------
/.github/workflows/golangci-lint.yml:
--------------------------------------------------------------------------------
 1 | name: golangci-lint
 2 | on:
 3 |   push:
 4 |     tags:
 5 |       - v*
 6 |     branches:
 7 |       - master
 8 |   pull_request:
 9 | jobs:
10 |   golangci:
11 |     name: lint
12 |     runs-on: ubuntu-latest
13 |     steps:
14 |       - uses: actions/checkout@v4.2.2
15 |       - name: golangci-lint
16 |         uses: golangci/golangci-lint-action@v8
17 |         with:
18 |           # Required: the version of golangci-lint is required and must be
19 |           # specified without patch version: we always use the latest patch version.
20 |           # https://github.com/golangci/golangci-lint/releases
21 |           version: latest
22 |           args: ./... --timeout=10m
23 | 


--------------------------------------------------------------------------------
/.github/workflows/goreleaser.yml:
--------------------------------------------------------------------------------
 1 | name: goreleaser
 2 | 
 3 | on:
 4 |   push:
 5 |     tags:
 6 |       - '*'
 7 | 
 8 | jobs:
 9 |   goreleaser:
10 |     runs-on: ubuntu-24.04
11 |     steps:
12 |       - name: Checkout
13 |         uses: actions/checkout@v4.2.2
14 |         with:
15 |           fetch-depth: 0
16 |       - name: Set up Go
17 |         uses: actions/setup-go@v5.5
18 |         with:
19 |           go-version-file: 'go.mod'
20 |       - name: Run GoReleaser
21 |         uses: goreleaser/goreleaser-action@v6.3.0
22 |         with:
23 |           version: 2.10.2
24 |           args: release --clean
25 |         env:
26 |           GITHUB_TOKEN: ${{ secrets.GH_PAT }}
27 | 


--------------------------------------------------------------------------------
/.github/workflows/pr-checks.yml:
--------------------------------------------------------------------------------
 1 | name: "PR Checks"
 2 | 
 3 | on:
 4 |   pull_request:
 5 |     branches:
 6 |       - master
 7 | 
 8 | jobs:
 9 |   goreleaser:
10 |     runs-on: ubuntu-24.04
11 |     steps:
12 |       - name: "Checkout code"
13 |         uses: actions/checkout@v4.2.2
14 |         with:
15 |           fetch-depth: 0
16 |       - name: "Set up Go"
17 |         uses: actions/setup-go@v5.5.0
18 |         with:
19 |           go-version-file: 'go.mod'
20 |       - name: Run GoReleaser
21 |         uses: goreleaser/goreleaser-action@v6.3.0
22 |         with:
23 |           version: 2.10.2
24 |           args: release --snapshot
25 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | ### Go ###
 2 | # Binaries for programs and plugins
 3 | *.exe
 4 | *.exe~
 5 | *.dll
 6 | ftw*
 7 | *.so
 8 | *.dylib
 9 | 
10 | # Test binary, build with `go test -c`
11 | *.test
12 | 
13 | # Output of the go coverage tool, specifically when used with LiteIDE
14 | *.out
15 | 
16 | # Misc
17 | .DS_Store
18 | gcal/client_secret.json
19 | gspreadsheets/client_secret.json
20 | profile.pdf
21 | report.*
22 | .vscode
23 | 
24 | # All things node
25 | node_modules/
26 | package-lock.json
27 | 
28 | #intellij idea
29 | .idea/
30 | 
31 | dist/*
32 | bin/
33 | 


--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/.gitmodules


--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
 1 | version: "2"
 2 | 
 3 | run:
 4 |   timeout: 3m
 5 | 
 6 | linters:
 7 |   enable:
 8 |     - govet
 9 |     - errcheck
10 |     - staticcheck
11 |     - unconvert
12 |   exclusions:
13 |     rules:
14 |       - linters:
15 |         - errcheck
16 |         source: "^\\s*defer\\s+"
17 | 
18 | formatters:
19 |   enable:
20 |     - gofmt
21 | 


--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
 1 | version: 2
 2 | 
 3 | env:
 4 |   - GO111MODULE=on
 5 |   - GOPROXY="https://proxy.golang.org,direct"
 6 | 
 7 | archives:
 8 |   - id: default
 9 |     wrap_in_directory: true
10 | 
11 | builds:
12 |   - binary: wtfutil
13 |     goos:
14 |       - darwin
15 |       - linux
16 |     goarch:
17 |       - amd64
18 |       - arm
19 |       - arm64
20 | 
21 | before:
22 |   hooks:
23 |     - make install
24 | 
25 | homebrew_casks:
26 |   - name: wtfutil
27 |     conflicts:
28 |       - formula: wtfutil
29 |     homepage: 'https://wtfutil.com'
30 |     description: 'The personal information dashboard for your terminal.'
31 |     repository:
32 |       owner: wtfutil
33 |       name: homebrew-wtfutil
34 |     hooks:
35 |       post:
36 |         # This hook is needed until this binary is signed and notarized
37 |         install: |
38 |           if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0
39 |             system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/wtfutil"]
40 |           end
41 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | language: go
 2 | 
 3 | go:
 4 |   - "1.19.x"
 5 | 
 6 | before_install:
 7 |   # Make sure travis builds work for forks
 8 |   - mkdir -p $TRAVIS_BUILD_DIR $GOPATH/src/github.com/wtfutil
 9 |   - test ! -d $GOPATH/src/github.com/wtfutil/wtf && mv $TRAVIS_BUILD_DIR $GOPATH/src/github.com/wtfutil/wtf || true
10 |   - export TRAVIS_BUILD_DIR=$HOME/gopath/src/github.com/wtfutil/wtf
11 |   - cd $HOME/gopath/src/github.com/wtfutil/wtf
12 |   - export GOPROXY="https://proxy.golang.org,direct"
13 | 
14 | script: go get ./... && ./scripts/check-uncommitted-vendor-files.sh && go test -v github.com/wtfutil/wtf/...
15 | 


--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
 1 | FROM golang:1.24-alpine as build
 2 | 
 3 | ARG version=master
 4 | 
 5 | RUN apk add git make ncurses && \
 6 |     git clone https://github.com/wtfutil/wtf.git $GOPATH/src/github.com/wtfutil/wtf && \
 7 |     cd $GOPATH/src/github.com/wtfutil/wtf && \
 8 |     git checkout $version
 9 | 
10 | ENV GOPROXY=https://proxy.golang.org,direct
11 | ENV GO111MODULE=on
12 | ENV GOSUMDB=off
13 | 
14 | WORKDIR $GOPATH/src/github.com/wtfutil/wtf
15 | 
16 | ENV PATH=$PATH:./bin
17 | 
18 | RUN make build
19 | 
20 | FROM alpine
21 | 
22 | COPY --from=build /go/src/github.com/wtfutil/wtf/bin/wtfutil /usr/local/bin/
23 | RUN adduser -h /config -DG users -u 20000 wtf
24 | 
25 | USER wtf
26 | ENTRYPOINT ["wtfutil"]
27 | 


--------------------------------------------------------------------------------
/Dockerfile.build:
--------------------------------------------------------------------------------
 1 | FROM golang:1.24 as build
 2 | 
 3 | ARG version=master
 4 | 
 5 | RUN git clone https://github.com/wtfutil/wtf.git $GOPATH/src/github.com/wtfutil/wtf && \
 6 |     cd $GOPATH/src/github.com/wtfutil/wtf && \
 7 |     git checkout $version
 8 | 
 9 | ENV GOPROXY=https://proxy.golang.org,direct
10 | ENV GO111MODULE=on
11 | ENV GOSUMDB=off
12 | 
13 | WORKDIR $GOPATH/src/github.com/wtfutil/wtf
14 | 
15 | ENV PATH=$PATH:./bin
16 | 
17 | RUN make build && \
18 |     cp bin/wtfutil /usr/local/bin/
19 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 | 
3 | To file a security issue, open a new Issue in the Issues tab.
4 | 


--------------------------------------------------------------------------------
/_sample_configs/bargraph_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/_sample_configs/bargraph_config.png


--------------------------------------------------------------------------------
/_sample_configs/bargraph_config.yml:
--------------------------------------------------------------------------------
 1 | wtf:
 2 |   colors:
 3 |     border:
 4 |       focusable: darkslateblue
 5 |       focused: orange
 6 |       normal: gray
 7 |   grid:
 8 |     columns: [40, 40]
 9 |     rows: [13, 13, 4]
10 |   refreshInterval: 1
11 |   mods:
12 |     bargraph:
13 |       enabled: true
14 |       graphIcon: "💀"
15 |       graphStars: 25
16 |       position:
17 |         top: 1
18 |         left: 0
19 |         height: 2
20 |         width: 2
21 |       refreshInterval: 30


--------------------------------------------------------------------------------
/_sample_configs/dynamic_sizing.yml:
--------------------------------------------------------------------------------
 1 | wtf:
 2 |   mods:
 3 |     battery:
 4 |       type: power
 5 |       title: "⚡️"
 6 |       enabled: true
 7 |       position:
 8 |         top: 0
 9 |         left: 0
10 |         height: 1
11 |         width: 1
12 |       refreshInterval: 15
13 |     security_info:
14 |       type: security
15 |       enabled: true
16 |       position:
17 |         top: 0
18 |         left: 1
19 |         height: 1
20 |         width: 1
21 |       refreshInterval: 3600


--------------------------------------------------------------------------------
/_sample_configs/kubernetes_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/_sample_configs/kubernetes_config.png


--------------------------------------------------------------------------------
/_sample_configs/kubernetes_config.yml:
--------------------------------------------------------------------------------
 1 | wtf:
 2 |   colors:
 3 |     border:
 4 |       focusable: darkslateblue
 5 |       focused: orange
 6 |       normal: gray
 7 |   grid:
 8 |     columns: [32, 32, 32, 32, 32, 32]
 9 |     rows: [10, 10, 10, 10, 10, 10]
10 |   refreshInterval: 2
11 |   mods:
12 |     kubernetes:
13 |       enabled: true
14 |       kubeconfig: /Users/testuser/.kube/config
15 |       namespaces: ["demo", "kube-system"]
16 |       objects: ["nodes","deployments", "pods"]
17 |       position:
18 |         top: 0
19 |         left: 0
20 |         height: 6
21 |         width: 3
22 | 


--------------------------------------------------------------------------------
/_sample_configs/sample_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/_sample_configs/sample_config.png


--------------------------------------------------------------------------------
/_sample_configs/small_config.yml:
--------------------------------------------------------------------------------
 1 | wtf:
 2 |   grid:
 3 |     columns: [20, 20]
 4 |     rows: [3, 3]
 5 |   refreshInterval: 1
 6 |   mods:
 7 |     uptime:
 8 |       type: cmdrunner
 9 |       args: []
10 |       cmd: "uptime"
11 |       enabled: true
12 |       position:
13 |         top: 0
14 |         left: 0
15 |         height: 1
16 |         width: 1
17 |       refreshInterval: 30
18 | 


--------------------------------------------------------------------------------
/_sample_configs/uniconfig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/_sample_configs/uniconfig.png


--------------------------------------------------------------------------------
/_sample_configs/uniconfig.yml:
--------------------------------------------------------------------------------
 1 | wtf:
 2 |   colors:
 3 |     background: black
 4 |     border:
 5 |       focusable: darkslateblue
 6 |   grid:
 7 |     columns: [40, 40]
 8 |     rows: [16]
 9 |   refreshInterval: 1
10 |   mods:
11 |     americas_time:
12 |       title: "Americas"
13 |       type: clocks
14 |       enabled: true
15 |       locations:
16 |         UTC: "Etc/UTC"
17 |         Vancouver: "America/Vancouver"
18 |         New_York: "America/New_York"
19 |         Sao_Paolo: "America/Sao_Paulo"
20 |         Denver: "America/Denver"
21 |         Iqaluit: "America/Iqaluit"
22 |         Bahamas: "America/Nassau"
23 |         Chicago: "America/Chicago"
24 |       position:
25 |         top: 0
26 |         left: 0
27 |         height: 1
28 |         width: 1
29 |       refreshInterval: 15
30 |       sort: "chronological"
31 |     textfile:
32 |       enabled: true
33 |       filePaths:
34 |         - "~/.config/wtf/config.yml"
35 |       format: true
36 |       formatStyle: "vim"
37 |       position:
38 |         top: 0
39 |         left: 1
40 |         height: 1
41 |         width: 1
42 |       refreshInterval: 15
43 | 


--------------------------------------------------------------------------------
/app/scheduler.go:
--------------------------------------------------------------------------------
 1 | package app
 2 | 
 3 | import (
 4 | 	"time"
 5 | 
 6 | 	"github.com/wtfutil/wtf/wtf"
 7 | )
 8 | 
 9 | // Schedule kicks off the first refresh of a module's data and then queues the rest of the
10 | // data refreshes on a timer
11 | func Schedule(widget wtf.Wtfable) {
12 | 	widget.Refresh()
13 | 
14 | 	interval := widget.CommonSettings().RefreshInterval
15 | 
16 | 	if interval <= 0 {
17 | 		return
18 | 	}
19 | 
20 | 	timer := time.NewTicker(interval)
21 | 
22 | 	for {
23 | 		select {
24 | 		case <-timer.C:
25 | 			if widget.Enabled() {
26 | 				widget.Refresh()
27 | 			} else {
28 | 				timer.Stop()
29 | 				return
30 | 			}
31 | 		case quit := <-widget.QuitChan():
32 | 			if quit {
33 | 				timer.Stop()
34 | 				return
35 | 			}
36 | 		}
37 | 	}
38 | }
39 | 


--------------------------------------------------------------------------------
/cfg/default_color_theme_test.go:
--------------------------------------------------------------------------------
 1 | package cfg
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func Test_NewDefaultColorTheme(t *testing.T) {
10 | 	theme := NewDefaultColorTheme()
11 | 
12 | 	assert.Equal(t, "orange", theme.Focused)
13 | 	assert.Equal(t, "red", theme.Subheading)
14 | 	assert.Equal(t, "transparent", theme.Background)
15 | }
16 | 
17 | func Test_NewDefaultColorConfig(t *testing.T) {
18 | 	cfg, err := NewDefaultColorConfig()
19 | 
20 | 	assert.Nil(t, err)
21 | 
22 | 	assert.Equal(t, "orange", cfg.UString("bordertheme.focused"))
23 | 	assert.Equal(t, "red", cfg.UString("texttheme.subheading"))
24 | 	assert.Equal(t, "transparent", cfg.UString("widgettheme.background"))
25 | 	assert.Equal(t, "", cfg.UString("widgettheme.missing"))
26 | }
27 | 


--------------------------------------------------------------------------------
/cfg/parsers.go:
--------------------------------------------------------------------------------
 1 | package cfg
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"time"
 6 | 
 7 | 	"github.com/olebedev/config"
 8 | )
 9 | 
10 | // ParseAsMapOrList takes a configuration key and attempts to parse it first as a map
11 | // and then as a list. Map entries are concatenated as "key/value"
12 | func ParseAsMapOrList(ymlConfig *config.Config, configKey string) []string {
13 | 	result := []string{}
14 | 
15 | 	mapItems, err := ymlConfig.Map(configKey)
16 | 	if err == nil {
17 | 		for key, value := range mapItems {
18 | 			result = append(result, fmt.Sprintf("%s/%s", value, key))
19 | 		}
20 | 		return result
21 | 	}
22 | 
23 | 	listItems := ymlConfig.UList(configKey)
24 | 	for _, listItem := range listItems {
25 | 		result = append(result, listItem.(string))
26 | 	}
27 | 
28 | 	return result
29 | }
30 | 
31 | // ParseTimeString takes a configuration key and attempts to parse it first as an int
32 | // and then as a duration (int + time unit)
33 | func ParseTimeString(cfg *config.Config, configKey string, defaultValue string) time.Duration {
34 | 	i, err := cfg.Int(configKey)
35 | 	if err == nil {
36 | 		return time.Duration(i) * time.Second
37 | 	}
38 | 
39 | 	str := cfg.UString(configKey, defaultValue)
40 | 	d, err := time.ParseDuration(str)
41 | 	if err == nil {
42 | 		return d
43 | 	}
44 | 
45 | 	return time.Second
46 | }
47 | 


--------------------------------------------------------------------------------
/cfg/position_validation.go:
--------------------------------------------------------------------------------
 1 | package cfg
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 
 6 | 	"github.com/logrusorgru/aurora/v4"
 7 | )
 8 | 
 9 | // Common examples of invalid position configuration are:
10 | //
11 | //	position:
12 | //	  top: -3
13 | //	  left: 2
14 | //	  width: 0
15 | //	  height: 1
16 | //
17 | //	position:
18 | //	  top: 3
19 | //	  width: 2
20 | //	  height: 1
21 | //
22 | //	position:
23 | //	  top: 3
24 | //	  # left: 2
25 | //	  width: 2
26 | //	  height: 1
27 | //
28 | //	position:
29 | //	top: 3
30 | //	left: 2
31 | //	width: 2
32 | //	height: 1
33 | type positionValidation struct {
34 | 	err    error
35 | 	name   string
36 | 	intVal int
37 | }
38 | 
39 | func (posVal *positionValidation) Error() error {
40 | 	return posVal.err
41 | }
42 | 
43 | func (posVal *positionValidation) HasError() bool {
44 | 	return posVal.err != nil
45 | }
46 | 
47 | func (posVal *positionValidation) IntValue() int {
48 | 	return posVal.intVal
49 | }
50 | 
51 | // String returns the Stringer representation of the positionValidation
52 | func (posVal *positionValidation) String() string {
53 | 	return fmt.Sprintf("Invalid value for %s:\t%d", aurora.Yellow(posVal.name), posVal.intVal)
54 | }
55 | 
56 | /* -------------------- Unexported Functions -------------------- */
57 | 
58 | func newPositionValidation(name string, intVal int, err error) *positionValidation {
59 | 	posVal := &positionValidation{
60 | 		err:    err,
61 | 		name:   name,
62 | 		intVal: intVal,
63 | 	}
64 | 
65 | 	return posVal
66 | }
67 | 


--------------------------------------------------------------------------------
/cfg/position_validation_test.go:
--------------------------------------------------------------------------------
 1 | package cfg
 2 | 
 3 | import (
 4 | 	"errors"
 5 | 	"testing"
 6 | 
 7 | 	"github.com/stretchr/testify/assert"
 8 | )
 9 | 
10 | var (
11 | 	posVal = &positionValidation{
12 | 		err:    errors.New("Busted"),
13 | 		name:   "top",
14 | 		intVal: -3,
15 | 	}
16 | )
17 | 
18 | func Test_Attributes(t *testing.T) {
19 | 	assert.EqualError(t, posVal.Error(), "Busted")
20 | 	assert.Equal(t, true, posVal.HasError())
21 | 	assert.Equal(t, -3, posVal.IntValue())
22 | 
23 | 	assert.Contains(t, posVal.String(), "Invalid")
24 | 	assert.Contains(t, posVal.String(), "top")
25 | 	assert.Contains(t, posVal.String(), "-3")
26 | }
27 | 


--------------------------------------------------------------------------------
/cfg/validatable.go:
--------------------------------------------------------------------------------
 1 | package cfg
 2 | 
 3 | // Validatable is implemented by any value that validates a configuration setting
 4 | type Validatable interface {
 5 | 	Error() error
 6 | 	HasError() bool
 7 | 	String() string
 8 | 	IntValue() int
 9 | }
10 | 


--------------------------------------------------------------------------------
/cfg/validations.go:
--------------------------------------------------------------------------------
 1 | package cfg
 2 | 
 3 | // Validations represent a collection of config setting validations
 4 | type Validations struct {
 5 | 	validations map[string]Validatable
 6 | }
 7 | 
 8 | // NewValidations creates and returns an instance of Validations
 9 | func NewValidations() *Validations {
10 | 	vals := &Validations{
11 | 		validations: make(map[string]Validatable),
12 | 	}
13 | 
14 | 	return vals
15 | }
16 | 
17 | func (vals *Validations) append(key string, posVal Validatable) {
18 | 	vals.validations[key] = posVal
19 | }
20 | 
21 | func (vals *Validations) intValueFor(key string) int {
22 | 	val := vals.validations[key]
23 | 	if val != nil {
24 | 		return val.IntValue()
25 | 	}
26 | 
27 | 	return 0
28 | }
29 | 


--------------------------------------------------------------------------------
/cfg/validations_test.go:
--------------------------------------------------------------------------------
 1 | package cfg
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | var (
10 | 	vals = NewValidations()
11 | )
12 | 
13 | func Test_intValueFor(t *testing.T) {
14 | 	vals.append("left", newPositionValidation("left", 3, nil))
15 | 
16 | 	tests := []struct {
17 | 		name     string
18 | 		key      string
19 | 		expected int
20 | 	}{
21 | 		{
22 | 			name:     "with valid key",
23 | 			key:      "left",
24 | 			expected: 3,
25 | 		},
26 | 		{
27 | 			name:     "with invalid key",
28 | 			key:      "cat",
29 | 			expected: 0,
30 | 		},
31 | 	}
32 | 
33 | 	for _, tt := range tests {
34 | 		t.Run(tt.name, func(t *testing.T) {
35 | 			assert.Equal(t, tt.expected, vals.intValueFor(tt.key))
36 | 		})
37 | 	}
38 | }
39 | 


--------------------------------------------------------------------------------
/checklist/checklist_item_test.go:
--------------------------------------------------------------------------------
 1 | package checklist
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func testChecklistItem() *ChecklistItem {
10 | 	item := NewChecklistItem(
11 | 		false,
12 | 		nil,
13 | 		make([]string, 0),
14 | 		"test",
15 | 		"",
16 | 		"",
17 | 	)
18 | 	return item
19 | }
20 | 
21 | func Test_CheckMark(t *testing.T) {
22 | 	item := testChecklistItem()
23 | 	assert.Equal(t, " ", item.CheckMark())
24 | 
25 | 	item.Toggle()
26 | 	assert.Equal(t, "x", item.CheckMark())
27 | }
28 | 
29 | func Test_Toggle(t *testing.T) {
30 | 	item := testChecklistItem()
31 | 	assert.Equal(t, false, item.Checked)
32 | 
33 | 	item.Toggle()
34 | 	assert.Equal(t, true, item.Checked)
35 | 
36 | 	item.Toggle()
37 | 	assert.Equal(t, false, item.Checked)
38 | }
39 | 


--------------------------------------------------------------------------------
/generator/settings.tpl:
--------------------------------------------------------------------------------
 1 | package {{(Lower .Name)}}
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "{{(.Name)}}"
11 | )
12 | 
13 | // Settings defines the configuration properties for this module
14 | type Settings struct {
15 | 	common *cfg.Common
16 | 
17 |     // Define your settings attributes here
18 | }
19 | 
20 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
21 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
22 | 	settings := Settings{
23 |         common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
24 | 
25 |         // Configure your settings attributes here. See http://github.com/olebedev/config for type details
26 | 	}
27 | 
28 | 	return &settings
29 | }


--------------------------------------------------------------------------------
/generator/textwidget.tpl:
--------------------------------------------------------------------------------
 1 | package {{(Lower .Name)}}
 2 | 
 3 | import (
 4 | 	"github.com/rivo/tview"
 5 | 	"github.com/wtfutil/wtf/view"
 6 | )
 7 | 
 8 | // Widget is the container for your module's data
 9 | type Widget struct {
10 | 	view.TextWidget
11 | 
12 | 	settings *Settings
13 | }
14 | 
15 | // NewWidget creates and returns an instance of Widget
16 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages *tview.Pages, settings *Settings) *Widget {
17 | 	widget := Widget{
18 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, pages, settings.common),
19 | 
20 | 		settings: settings,
21 | 	}
22 | 
23 | 	return &widget
24 | }
25 | 
26 | /* -------------------- Exported Functions -------------------- */
27 | 
28 | // Refresh updates the onscreen contents of the widget
29 | func (widget *Widget) Refresh() {
30 | 
31 |     // The last call should always be to the display function
32 |     widget.display()
33 | }
34 | 
35 | /* -------------------- Unexported Functions -------------------- */
36 | 
37 | func (widget *Widget) content() string {
38 | 	return "This is my widget"
39 | }
40 | 
41 | func (widget *Widget) display() {
42 | 	widget.Redraw(func() (string, string, bool) {
43 | 		return widget.CommonSettings().Title, widget.content(), false
44 | 	})
45 | }
46 | 


--------------------------------------------------------------------------------
/help/help.go:
--------------------------------------------------------------------------------
 1 | package help
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/app"
 8 | 	"github.com/wtfutil/wtf/utils"
 9 | )
10 | 
11 | // Display displays the output of the --help argument
12 | func Display(moduleName string, cfg *config.Config) {
13 | 	if moduleName == "" {
14 | 		fmt.Println("\n  --module takes a module name as an argument, i.e: '--module=github'")
15 | 	} else {
16 | 		fmt.Printf("%s\n", helpFor(moduleName, cfg))
17 | 	}
18 | }
19 | 
20 | func helpFor(moduleName string, cfg *config.Config) string {
21 | 	err := cfg.Set("wtf.mods."+moduleName+".enabled", true)
22 | 	if err != nil {
23 | 		return ""
24 | 	}
25 | 
26 | 	widget := app.MakeWidget(nil, nil, moduleName, cfg, nil)
27 | 
28 | 	// Since we are forcing enabled config, if no module
29 | 	// exists, we will get the unknown one
30 | 	if widget.CommonSettings().Title == "Unknown" {
31 | 		return "Unable to find module " + moduleName
32 | 	}
33 | 
34 | 	result := ""
35 | 	result += utils.StripColorTags(widget.HelpText())
36 | 	result += "\n"
37 | 	result += "Configuration Attributes"
38 | 	result += widget.ConfigText()
39 | 	return result
40 | }
41 | 


--------------------------------------------------------------------------------
/images/dude_wtf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/dude_wtf.png


--------------------------------------------------------------------------------
/images/logo_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/logo_transparent.png


--------------------------------------------------------------------------------
/images/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/screenshot.jpg


--------------------------------------------------------------------------------
/images/sponsors/airbrake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/sponsors/airbrake.png


--------------------------------------------------------------------------------
/images/sponsors/robusta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/sponsors/robusta.png


--------------------------------------------------------------------------------
/images/sponsors/warp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/sponsors/warp.png


--------------------------------------------------------------------------------
/images/sponsors/warp2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/sponsors/warp2.png


--------------------------------------------------------------------------------
/images/sponsors/warp3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfutil/wtf/930a94a305d19077dbf26402eb7c6b6de2169adc/images/sponsors/warp3.png


--------------------------------------------------------------------------------
/logger/log.go:
--------------------------------------------------------------------------------
 1 | package logger
 2 | 
 3 | import (
 4 | 	"log"
 5 | 	"os"
 6 | 	"path/filepath"
 7 | )
 8 | 
 9 | /* -------------------- Exported Functions -------------------- */
10 | 
11 | func Log(msg string) {
12 | 	if LogFileMissing() {
13 | 		return
14 | 	}
15 | 
16 | 	f, err := os.OpenFile(LogFilePath(), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
17 | 	if err != nil {
18 | 		log.Fatalf("error opening file: %v", err)
19 | 	}
20 | 	defer func() { _ = f.Close() }()
21 | 
22 | 	log.SetOutput(f)
23 | 	log.Println(msg)
24 | }
25 | 
26 | func LogFileMissing() bool {
27 | 	return LogFilePath() == ""
28 | }
29 | 
30 | func LogFilePath() string {
31 | 	dir, err := os.UserHomeDir()
32 | 	if err != nil {
33 | 		return ""
34 | 	}
35 | 
36 | 	return filepath.Join(dir, ".config", "wtf", "log.txt")
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/airbrake/group_info_table.go:
--------------------------------------------------------------------------------
 1 | package airbrake
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"strconv"
 6 | 
 7 | 	"github.com/wtfutil/wtf/view"
 8 | )
 9 | 
10 | type groupInfoTable struct {
11 | 	group       *Group
12 | 	propertyMap map[string]string
13 | 
14 | 	colWidth0   int
15 | 	colWidth1   int
16 | 	tableHeight int
17 | }
18 | 
19 | func newGroupInfoTable(g *Group) *groupInfoTable {
20 | 	propTable := &groupInfoTable{
21 | 		group: g,
22 | 
23 | 		colWidth0:   20,
24 | 		colWidth1:   51,
25 | 		tableHeight: 15,
26 | 	}
27 | 
28 | 	propTable.propertyMap = propTable.buildPropertyMap()
29 | 
30 | 	return propTable
31 | }
32 | 
33 | func (propTable *groupInfoTable) buildPropertyMap() map[string]string {
34 | 	propMap := map[string]string{}
35 | 
36 | 	g := propTable.group
37 | 	if g == nil {
38 | 		return propMap
39 | 	}
40 | 	propMap["1. First Seen"] = g.CreatedAt
41 | 	propMap["2. Last Seen"] = g.LastNoticeAt
42 | 	propMap["3. Occurrences"] = strconv.Itoa(int(g.NoticeCount))
43 | 	propMap["4. Environment"] = g.Context.Environment
44 | 	propMap["5. Severity"] = g.Context.Severity
45 | 	propMap["6. Muted"] = fmt.Sprintf("%v", g.Muted)
46 | 	propMap["7. File"] = g.File()
47 | 
48 | 	return propMap
49 | }
50 | 
51 | func (propTable *groupInfoTable) render() string {
52 | 	tbl := view.NewInfoTable(
53 | 		[]string{"Property", "Value"},
54 | 		propTable.propertyMap,
55 | 		propTable.colWidth0,
56 | 		propTable.colWidth1,
57 | 		propTable.tableHeight,
58 | 	)
59 | 
60 | 	return tbl.Render()
61 | }
62 | 


--------------------------------------------------------------------------------
/modules/airbrake/keyboard.go:
--------------------------------------------------------------------------------
 1 | package airbrake
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("o", widget.openGroup, "Open group in browser")
10 | 	widget.SetKeyboardChar("s", widget.resolveGroup, "Resolve group")
11 | 	widget.SetKeyboardChar("m", widget.muteGroup, "Mute group")
12 | 	widget.SetKeyboardChar("u", widget.unmuteGroup, "Unmute group")
13 | 	widget.SetKeyboardChar("t", widget.toggleDisplayText, "Toggle between title and compare views")
14 | 
15 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
16 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
17 | 
18 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
19 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
20 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
21 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.viewGroup, "View group")
22 | }
23 | 


--------------------------------------------------------------------------------
/modules/airbrake/result_table.go:
--------------------------------------------------------------------------------
 1 | package airbrake
 2 | 
 3 | import (
 4 | 	"github.com/wtfutil/wtf/utils"
 5 | 	"github.com/wtfutil/wtf/view"
 6 | )
 7 | 
 8 | type resultTable struct {
 9 | 	propertyMap map[string]string
10 | 
11 | 	colWidth0   int
12 | 	colWidth1   int
13 | 	tableHeight int
14 | }
15 | 
16 | func newResultTable(result, message string) *resultTable {
17 | 	propTable := &resultTable{
18 | 		colWidth0:   20,
19 | 		colWidth1:   51,
20 | 		tableHeight: 15,
21 | 	}
22 | 	propTable.propertyMap = map[string]string{result: message}
23 | 
24 | 	return propTable
25 | }
26 | 
27 | func (propTable *resultTable) render() string {
28 | 	tbl := view.NewInfoTable(
29 | 		[]string{"Result", "Message"},
30 | 		propTable.propertyMap,
31 | 		propTable.colWidth0,
32 | 		propTable.colWidth1,
33 | 		propTable.tableHeight,
34 | 	)
35 | 
36 | 	return tbl.Render() + utils.CenterText("Esc to close", 80)
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/airbrake/settings.go:
--------------------------------------------------------------------------------
 1 | package airbrake
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"strconv"
 6 | 
 7 | 	"github.com/olebedev/config"
 8 | 	"github.com/wtfutil/wtf/cfg"
 9 | )
10 | 
11 | const (
12 | 	defaultFocusable = true
13 | 	defaultTitle     = "Airbrake"
14 | )
15 | 
16 | type Settings struct {
17 | 	*cfg.Common
18 | 
19 | 	projectID int    `help:"The id of your Airbrake project."`
20 | 	authToken string `help:"The token that allows accessing Airbrake API"`
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 	settings := Settings{
25 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle,
26 | 			defaultFocusable, ymlConfig, globalConfig),
27 | 		projectID: ymlConfig.UInt("projectID", getProjectID()),
28 | 		authToken: ymlConfig.UString("authToken", os.Getenv("AIRBRAKE_USER_KEY")),
29 | 	}
30 | 
31 | 	cfg.ModuleSecret(name, globalConfig, &settings.authToken).Load()
32 | 
33 | 	return &settings
34 | }
35 | 
36 | func getProjectID() int {
37 | 	projectID, err := strconv.ParseInt(os.Getenv("AIRBRAKE_PROJECT_ID"), 10, 32)
38 | 	if err != nil {
39 | 		return 0
40 | 	}
41 | 
42 | 	return int(projectID)
43 | }
44 | 


--------------------------------------------------------------------------------
/modules/airbrake/util.go:
--------------------------------------------------------------------------------
 1 | package airbrake
 2 | 
 3 | func reverseString(s string) string {
 4 | 	r := []rune(s)
 5 | 	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
 6 | 		r[i], r[j] = r[j], r[i]
 7 | 	}
 8 | 	return string(r)
 9 | }
10 | 


--------------------------------------------------------------------------------
/modules/asana/keyboard.go:
--------------------------------------------------------------------------------
 1 | package asana
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next task")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous task")
11 | 	widget.SetKeyboardChar("q", widget.Unselect, "Unselect task")
12 | 	widget.SetKeyboardChar("o", widget.openTask, "Open task in browser")
13 | 	widget.SetKeyboardChar("x", widget.toggleTaskCompletion, "Toggles the task's completion state")
14 | 	widget.SetKeyboardChar("?", widget.ShowHelp, "Shows help")
15 | 
16 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next task")
17 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous task")
18 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Unselect task")
19 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openTask, "Open task in browser")
20 | }
21 | 


--------------------------------------------------------------------------------
/modules/azuredevops/example-conf.yml:
--------------------------------------------------------------------------------
 1 | wtf:
 2 |   colors:
 3 |     # background: black
 4 |     # foreground: blue
 5 |     border:
 6 |       focusable: darkslateblue
 7 |       focused: orange
 8 |       normal: gray
 9 |     checked: yellow
10 |     highlight: 
11 |       fore: black
12 |       back: gray
13 |     rows:
14 |       even: yellow
15 |       odd: white
16 |   grid:
17 |     # How _wide_ the columns are, in terminal characters. In this case we have
18 |     # four columns, each of which are 35 characters wide.
19 |     # columns: [50, ]
20 |     # How _high_ the rows are, in terminal lines. In this case we have four rows
21 |     # that support ten line of text and one of four.
22 |     # rows: [50]
23 |   refreshInterval: 1
24 |   openFileUtil: "open"
25 |   mods:
26 |     azuredevops:
27 |       type: azuredevops
28 |       title: "💻"
29 |       enabled: true
30 |       position:
31 |         top: 0
32 |         left: 0
33 |         height: 3
34 |         width: 3
35 |       refreshInterval: 1
36 |       labelColor: lightblue # title label color (optional / default: white)
37 |       apiToken: "mysecret api token" # api key (required)
38 |       orgUrl: "https://dev.azure.com/myawesomecompany/" # url to your azure devops project (required)
39 |       prjectName: "the awesome project"  # name of your project (required)
40 |       maxRows: 3 #max rows to show (optional / default 3)
41 | 
42 |   


--------------------------------------------------------------------------------
/modules/azuredevops/settings.go:
--------------------------------------------------------------------------------
 1 | package azuredevops
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocus = false
12 | 	defaultTitle = "azuredevops"
13 | )
14 | 
15 | // Settings defines the configuration options for this module
16 | type Settings struct {
17 | 	*cfg.Common
18 | 
19 | 	apiToken    string `help:"Your Azure DevOps Access Token."`
20 | 	labelColor  string
21 | 	maxRows     int
22 | 	orgURL      string `help:"Your Azure DevOps organization URL."`
23 | 	projectName string
24 | }
25 | 
26 | // NewSettingsFromYAML creates and returns an instance of Settings with configuration options populated
27 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
28 | 	settings := Settings{
29 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocus, ymlConfig, globalConfig),
30 | 
31 | 		apiToken:    ymlConfig.UString("apiToken", os.Getenv("WTF_AZURE_DEVOPS_API_TOKEN")),
32 | 		labelColor:  ymlConfig.UString("labelColor", "white"),
33 | 		maxRows:     ymlConfig.UInt("maxRows", 3),
34 | 		orgURL:      ymlConfig.UString("orgURL", os.Getenv("WTF_AZURE_DEVOPS_ORG_URL")),
35 | 		projectName: ymlConfig.UString("projectName", os.Getenv("WTF_AZURE_DEVOPS_PROJECT_NAME")),
36 | 	}
37 | 
38 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiToken).
39 | 		Service(settings.orgURL).Load()
40 | 
41 | 	return &settings
42 | }
43 | 


--------------------------------------------------------------------------------
/modules/bamboohr/calendar.go:
--------------------------------------------------------------------------------
 1 | package bamboohr
 2 | 
 3 | type Calendar struct {
 4 | 	Items []Item `xml:"item"`
 5 | }
 6 | 
 7 | /* -------------------- Public Functions -------------------- */
 8 | 
 9 | func (calendar *Calendar) Holidays() []Item {
10 | 	return calendar.filteredItems("holiday")
11 | }
12 | 
13 | func (calendar *Calendar) ItemsByType(itemType string) []Item {
14 | 	if itemType == "timeOff" {
15 | 		return calendar.TimeOffs()
16 | 	}
17 | 
18 | 	return calendar.Holidays()
19 | }
20 | 
21 | func (calendar *Calendar) TimeOffs() []Item {
22 | 	return calendar.filteredItems("timeOff")
23 | }
24 | 
25 | /* -------------------- Private Functions -------------------- */
26 | 
27 | func (calendar *Calendar) filteredItems(itemType string) []Item {
28 | 	items := []Item{}
29 | 
30 | 	for _, item := range calendar.Items {
31 | 		if item.Type == itemType {
32 | 			items = append(items, item)
33 | 		}
34 | 	}
35 | 
36 | 	return items
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/bamboohr/employee.go:
--------------------------------------------------------------------------------
 1 | package bamboohr
 2 | 
 3 | /*
 4 | * Note: this currently implements the minimum number of fields to fulfill the Away functionality.
 5 | * Undoubtedly there are more fields than this to an employee
 6 |  */
 7 | type Employee struct {
 8 | 	ID   int    `xml:"id,attr"`
 9 | 	Name string `xml:",chardata"`
10 | }
11 | 


--------------------------------------------------------------------------------
/modules/bamboohr/item.go:
--------------------------------------------------------------------------------
 1 | package bamboohr
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 
 6 | 	"github.com/wtfutil/wtf/wtf"
 7 | )
 8 | 
 9 | type Item struct {
10 | 	Employee Employee `xml:"employee"`
11 | 	End      string   `xml:"end"`
12 | 	Holiday  string   `xml:"holiday"`
13 | 	Start    string   `xml:"start"`
14 | 	Type     string   `xml:"type,attr"`
15 | }
16 | 
17 | func (item *Item) String() string {
18 | 	return fmt.Sprintf("Item: %s, %s, %s, %s", item.Type, item.Employee.Name, item.Start, item.End)
19 | }
20 | 
21 | /* -------------------- Exported Functions -------------------- */
22 | 
23 | func (item *Item) IsOneDay() bool {
24 | 	return item.Start == item.End
25 | }
26 | 
27 | func (item *Item) Name() string {
28 | 	if (item.Employee != Employee{}) {
29 | 		return item.Employee.Name
30 | 	}
31 | 
32 | 	return item.Holiday
33 | }
34 | 
35 | func (item *Item) PrettyStart() string {
36 | 	return wtf.PrettyDate(item.Start)
37 | }
38 | 
39 | func (item *Item) PrettyEnd() string {
40 | 	return wtf.PrettyDate(item.End)
41 | }
42 | 


--------------------------------------------------------------------------------
/modules/bamboohr/request.go:
--------------------------------------------------------------------------------
 1 | package bamboohr
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"net/http"
 6 | )
 7 | 
 8 | func Request(apiKey string, apiURL string) ([]byte, error) {
 9 | 	req, err := http.NewRequest("GET", apiURL, http.NoBody)
10 | 	if err != nil {
11 | 		return nil, err
12 | 	}
13 | 
14 | 	req.SetBasicAuth(apiKey, "x")
15 | 
16 | 	client := &http.Client{}
17 | 	resp, err := client.Do(req)
18 | 	if err != nil {
19 | 		return nil, err
20 | 	}
21 | 	defer func() { _ = resp.Body.Close() }()
22 | 
23 | 	data, err := ParseBody(resp)
24 | 	if err != nil {
25 | 		return nil, err
26 | 	}
27 | 
28 | 	return data, err
29 | }
30 | 
31 | func ParseBody(resp *http.Response) ([]byte, error) {
32 | 	var buffer bytes.Buffer
33 | 	_, err := buffer.ReadFrom(resp.Body)
34 | 	if err != nil {
35 | 		return nil, err
36 | 	}
37 | 
38 | 	return buffer.Bytes(), nil
39 | }
40 | 


--------------------------------------------------------------------------------
/modules/bamboohr/settings.go:
--------------------------------------------------------------------------------
 1 | package bamboohr
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = false
12 | 	defaultTitle     = "BambooHR"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiKey    string `help:"Your BambooHR API token."`
19 | 	subdomain string `help:"Your BambooHR API subdomain name."`
20 | }
21 | 
22 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
23 | 	settings := Settings{
24 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
25 | 
26 | 		apiKey:    ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_BAMBOO_HR_TOKEN"))),
27 | 		subdomain: ymlConfig.UString("subdomain", os.Getenv("WTF_BAMBOO_HR_SUBDOMAIN")),
28 | 	}
29 | 
30 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
31 | 
32 | 	return &settings
33 | }
34 | 


--------------------------------------------------------------------------------
/modules/bargraph/settings.go:
--------------------------------------------------------------------------------
 1 | package bargraph
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Bargraph"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	return &settings
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/buildkite/keyboard.go:
--------------------------------------------------------------------------------
1 | package buildkite
2 | 
3 | func (widget *Widget) initializeKeyboardControls() {
4 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
5 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
6 | }
7 | 


--------------------------------------------------------------------------------
/modules/cds/favorites/keyboard.go:
--------------------------------------------------------------------------------
 1 | package cdsfavorites
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next workflow")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous workflow")
13 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next source")
14 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous source")
15 | 	widget.SetKeyboardChar("o", widget.openWorkflow, "Open workflow in browser")
16 | 
17 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next workflow")
18 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous workflow")
19 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next source")
20 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous source")
21 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openWorkflow, "Open workflow in browser")
22 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/cds/favorites/settings.go:
--------------------------------------------------------------------------------
 1 | package cdsfavorites
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | 	"github.com/wtfutil/wtf/utils"
 9 | )
10 | 
11 | const (
12 | 	defaultFocusable = true
13 | 	defaultTitle     = "CDS Favorites"
14 | )
15 | 
16 | // Settings defines the configuration properties for this module
17 | type Settings struct {
18 | 	*cfg.Common
19 | 
20 | 	token    string `help:"Your CDS API token."`
21 | 	apiURL   string `help:"Your CDS API URL."`
22 | 	uiURL    string
23 | 	hideTags []string `help:"Hide some workflow tags."`
24 | }
25 | 
26 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
27 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
28 | 	settings := Settings{
29 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
30 | 
31 | 		token:    ymlConfig.UString("token", ymlConfig.UString("token", os.Getenv("CDS_TOKEN"))),
32 | 		apiURL:   ymlConfig.UString("apiURL", os.Getenv("CDS_API_URL")),
33 | 		hideTags: utils.ToStrs(ymlConfig.UList("hideTags")),
34 | 	}
35 | 
36 | 	settings.SetDocumentationPath("cds/favorites")
37 | 
38 | 	return &settings
39 | }
40 | 


--------------------------------------------------------------------------------
/modules/cds/queue/keyboard.go:
--------------------------------------------------------------------------------
 1 | package cdsqueue
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next workflow")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous workflow")
13 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next filter")
14 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous filter")
15 | 	widget.SetKeyboardChar("o", widget.openWorkflow, "Open workflow in browser")
16 | 
17 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next workflow")
18 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous workflow")
19 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next filter")
20 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous filter")
21 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openWorkflow, "Open workflow in browser")
22 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/cds/queue/settings.go:
--------------------------------------------------------------------------------
 1 | package cdsqueue
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "CDS Queue"
13 | )
14 | 
15 | // Settings defines the configuration properties for this module
16 | type Settings struct {
17 | 	*cfg.Common
18 | 
19 | 	token  string `help:"Your CDS API token."`
20 | 	apiURL string `help:"Your CDS API URL."`
21 | 	uiURL  string
22 | }
23 | 
24 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
25 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
28 | 
29 | 		token:  ymlConfig.UString("token", ymlConfig.UString("token", os.Getenv("CDS_TOKEN"))),
30 | 		apiURL: ymlConfig.UString("apiURL", os.Getenv("CDS_API_URL")),
31 | 	}
32 | 
33 | 	settings.SetDocumentationPath("cds/queue")
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/cds/status/keyboard.go:
--------------------------------------------------------------------------------
 1 | package cdsstatus
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next line")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous line")
13 | 	widget.SetKeyboardChar("o", widget.openWorkflow, "Open status in browser")
14 | 
15 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next line")
16 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous line")
17 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openWorkflow, "Open status in browser")
18 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
19 | }
20 | 


--------------------------------------------------------------------------------
/modules/cds/status/settings.go:
--------------------------------------------------------------------------------
 1 | package cdsstatus
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "CDS Status"
13 | )
14 | 
15 | // Settings defines the configuration properties for this module
16 | type Settings struct {
17 | 	*cfg.Common
18 | 
19 | 	token  string `help:"Your CDS API token."`
20 | 	apiURL string `help:"Your CDS API URL."`
21 | 	uiURL  string
22 | }
23 | 
24 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
25 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
28 | 
29 | 		token:  ymlConfig.UString("token", ymlConfig.UString("token", os.Getenv("CDS_TOKEN"))),
30 | 		apiURL: ymlConfig.UString("apiURL", os.Getenv("CDS_API_URL")),
31 | 	}
32 | 
33 | 	settings.SetDocumentationPath("cds/status")
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/circleci/build.go:
--------------------------------------------------------------------------------
 1 | package circleci
 2 | 
 3 | type Build struct {
 4 | 	AuthorEmail string `json:"author_email"`
 5 | 	AuthorName  string `json:"author_name"`
 6 | 	Branch      string `json:"branch"`
 7 | 	BuildNum    int    `json:"build_num"`
 8 | 	Reponame    string `json:"reponame"`
 9 | 	Status      string `json:"status"`
10 | }
11 | 


--------------------------------------------------------------------------------
/modules/circleci/settings.go:
--------------------------------------------------------------------------------
 1 | package circleci
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = false
12 | 	defaultTitle     = "CircleCI"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiKey         string `help:"Your CircleCI API token."`
19 | 	numberOfBuilds int    `help:"The number of build, 10 by default"`
20 | }
21 | 
22 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
23 | 
24 | 	settings := Settings{
25 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
26 | 
27 | 		apiKey:         ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_CIRCLE_API_KEY"))),
28 | 		numberOfBuilds: ymlConfig.UInt("numberOfBuilds", 10),
29 | 	}
30 | 
31 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
32 | 
33 | 	return &settings
34 | }
35 | 


--------------------------------------------------------------------------------
/modules/clocks/clock.go:
--------------------------------------------------------------------------------
 1 | package clocks
 2 | 
 3 | import (
 4 | 	"strings"
 5 | 	"time"
 6 | )
 7 | 
 8 | type Clock struct {
 9 | 	Label    string
10 | 	Location *time.Location
11 | }
12 | 
13 | func NewClock(label string, timeLoc *time.Location) Clock {
14 | 	clock := Clock{
15 | 		Label:    label,
16 | 		Location: timeLoc,
17 | 	}
18 | 
19 | 	return clock
20 | }
21 | 
22 | func BuildClock(label string, location string) (clock Clock, err error) {
23 | 	timeLoc, err := time.LoadLocation(sanitizeLocation(location))
24 | 	if err != nil {
25 | 		return Clock{}, err
26 | 	}
27 | 	return NewClock(label, timeLoc), nil
28 | }
29 | 
30 | func (clock *Clock) Date(dateFormat string) string {
31 | 	return clock.LocalTime().Format(dateFormat)
32 | }
33 | 
34 | func (clock *Clock) LocalTime() time.Time {
35 | 	return clock.ToLocal(time.Now())
36 | }
37 | 
38 | func (clock *Clock) ToLocal(t time.Time) time.Time {
39 | 	return t.In(clock.Location)
40 | }
41 | 
42 | func (clock *Clock) Time(timeFormat string) string {
43 | 	return clock.LocalTime().Format(timeFormat)
44 | }
45 | 
46 | func sanitizeLocation(locStr string) string {
47 | 	return strings.ReplaceAll(locStr, " ", "_")
48 | }
49 | 


--------------------------------------------------------------------------------
/modules/clocks/clock_collection.go:
--------------------------------------------------------------------------------
 1 | package clocks
 2 | 
 3 | import (
 4 | 	"sort"
 5 | 	"time"
 6 | )
 7 | 
 8 | type ClockCollection struct {
 9 | 	Clocks []Clock
10 | }
11 | 
12 | func (clocks *ClockCollection) Sorted(sortOrder string) []Clock {
13 | 
14 | 	switch sortOrder {
15 | 	case "natural":
16 | 		// do nothing
17 | 	case "chronological":
18 | 		clocks.SortedChronologically()
19 | 	case "reversechronological":
20 | 		clocks.SortedReverseChronologically()
21 | 	default:
22 | 		clocks.SortedAlphabetically()
23 | 	}
24 | 
25 | 	return clocks.Clocks
26 | }
27 | 
28 | func (clocks *ClockCollection) SortedAlphabetically() {
29 | 	sort.Slice(clocks.Clocks, func(i, j int) bool {
30 | 		clock := clocks.Clocks[i]
31 | 		other := clocks.Clocks[j]
32 | 
33 | 		return clock.Label < other.Label
34 | 	})
35 | }
36 | 
37 | func (clocks *ClockCollection) SortedChronologically() {
38 | 	now := time.Now()
39 | 	sort.Slice(clocks.Clocks, func(i, j int) bool {
40 | 		clock := clocks.Clocks[i]
41 | 		other := clocks.Clocks[j]
42 | 
43 | 		return clock.ToLocal(now).String() < other.ToLocal(now).String()
44 | 	})
45 | }
46 | 
47 | func (clocks *ClockCollection) SortedReverseChronologically() {
48 | 	now := time.Now()
49 | 	sort.Slice(clocks.Clocks, func(i, j int) bool {
50 | 		clock := clocks.Clocks[i]
51 | 		other := clocks.Clocks[j]
52 | 
53 | 		return clock.ToLocal(now).String() > other.ToLocal(now).String()
54 | 	})
55 | }
56 | 


--------------------------------------------------------------------------------
/modules/clocks/display.go:
--------------------------------------------------------------------------------
 1 | package clocks
 2 | 
 3 | import "fmt"
 4 | 
 5 | func (widget *Widget) display(clocks []Clock, dateFormat string, timeFormat string) {
 6 | 	str := ""
 7 | 
 8 | 	locationWidth := 12
 9 | 	for _, clock := range clocks {
10 | 		if len(clock.Label) > locationWidth {
11 | 			locationWidth = len(clock.Label) + 2
12 | 		}
13 | 	}
14 | 
15 | 	if len(clocks) == 0 {
16 | 		str = fmt.Sprintf("\n%s", " no timezone data available")
17 | 	} else {
18 | 		for idx, clock := range clocks {
19 | 			str += fmt.Sprintf(
20 | 				" [%s]%-*s %-10s %7s[white]\n",
21 | 				widget.CommonSettings().RowColor(idx),
22 | 				locationWidth,
23 | 				clock.Label,
24 | 				clock.Time(timeFormat),
25 | 				clock.Date(dateFormat),
26 | 			)
27 | 		}
28 | 	}
29 | 
30 | 	widget.Redraw(func() (string, string, bool) { return widget.CommonSettings().Title, str, false })
31 | }
32 | 


--------------------------------------------------------------------------------
/modules/clocks/widget.go:
--------------------------------------------------------------------------------
 1 | package clocks
 2 | 
 3 | import (
 4 | 	"github.com/rivo/tview"
 5 | 	"github.com/wtfutil/wtf/view"
 6 | )
 7 | 
 8 | type Widget struct {
 9 | 	view.TextWidget
10 | 
11 | 	clockColl  ClockCollection
12 | 	dateFormat string
13 | 	timeFormat string
14 | 	settings   *Settings
15 | }
16 | 
17 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
18 | 	widget := Widget{
19 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
20 | 
21 | 		settings:   settings,
22 | 		dateFormat: settings.dateFormat,
23 | 		timeFormat: settings.timeFormat,
24 | 	}
25 | 
26 | 	widget.clockColl = widget.buildClockCollection()
27 | 
28 | 	return &widget
29 | }
30 | 
31 | /* -------------------- Exported Functions -------------------- */
32 | 
33 | // Refresh updates the onscreen contents of the widget
34 | func (widget *Widget) Refresh() {
35 | 	sortedClocks := widget.clockColl.Sorted(widget.settings.sort)
36 | 	widget.display(sortedClocks, widget.dateFormat, widget.timeFormat)
37 | }
38 | 
39 | /* -------------------- Unexported Functions -------------------- */
40 | 
41 | func (widget *Widget) buildClockCollection() ClockCollection {
42 | 	clockColl := ClockCollection{}
43 | 
44 | 	clockColl.Clocks = widget.settings.locations
45 | 
46 | 	return clockColl
47 | }
48 | 


--------------------------------------------------------------------------------
/modules/covid/cases.go:
--------------------------------------------------------------------------------
 1 | package covid
 2 | 
 3 | // Cases holds the latest cases
 4 | type Cases struct {
 5 | 	Latest Latest `json:"latest"`
 6 | }
 7 | 
 8 | // Latest holds the number of global confirmed cases and deaths due to Covid
 9 | type Latest struct {
10 | 	Confirmed int `json:"confirmed"`
11 | 	Deaths    int `json:"deaths"`
12 | 	// Not currently used but holds information about the country
13 | 	Locations []interface{} `json:"locations,omitempty"`
14 | }
15 | 


--------------------------------------------------------------------------------
/modules/covid/cases_test.go:
--------------------------------------------------------------------------------
 1 | package covid
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"testing"
 6 | )
 7 | 
 8 | func Test_CasesInclude(t *testing.T) {
 9 | 	// The api does not seem to return the correct recovered numbers
10 | 	responseBody := `{"latest":{"confirmed":3093619,"deaths":73018,"recovered":0},"locations":[]}`
11 | 	latestData := Cases{}
12 | 	_ = json.Unmarshal([]byte(responseBody), &latestData)
13 | 	expectedConfirmed := 3093619
14 | 	expectedDeaths := 73018
15 | 	actualConfirmed := latestData.Latest.Confirmed
16 | 	actualDeaths := latestData.Latest.Deaths
17 | 
18 | 	if expectedConfirmed != actualConfirmed {
19 | 		t.Errorf("\nexpected: %v\n     got: %v", expectedConfirmed, actualConfirmed)
20 | 	}
21 | 	if expectedDeaths != actualDeaths {
22 | 		t.Errorf("\nexpected: %v\n     got: %v", expectedDeaths, actualDeaths)
23 | 	}
24 | }
25 | 


--------------------------------------------------------------------------------
/modules/covid/settings.go:
--------------------------------------------------------------------------------
 1 | package covid
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Covid tracker"
11 | )
12 | 
13 | // Settings is the struct for this module's settings
14 | type Settings struct {
15 | 	*cfg.Common
16 | 
17 | 	countries []interface{} `help:"Countries (codes) from which to retrieve stats."`
18 | }
19 | 
20 | // NewSettingsFromYAML returns the settings from the config yaml file
21 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
22 | 
23 | 	settings := Settings{
24 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
25 | 
26 | 		// List of countries to retrieve stats from
27 | 		countries: ymlConfig.UList("countries"),
28 | 	}
29 | 
30 | 	return &settings
31 | }
32 | 


--------------------------------------------------------------------------------
/modules/cryptocurrency/bittrex/bittrex.go:
--------------------------------------------------------------------------------
 1 | package bittrex
 2 | 
 3 | type summaryList struct {
 4 | 	items []*bCurrency
 5 | }
 6 | 
 7 | // Base Currency
 8 | type bCurrency struct {
 9 | 	name        string
10 | 	displayName string
11 | 	markets     []*mCurrency
12 | }
13 | 
14 | // Market Currency
15 | type mCurrency struct {
16 | 	name string
17 | 	summaryInfo
18 | }
19 | 
20 | type summaryInfo struct {
21 | 	Low            string
22 | 	High           string
23 | 	Volume         string
24 | 	Last           string
25 | 	OpenSellOrders string
26 | 	OpenBuyOrders  string
27 | }
28 | 
29 | type summaryResponse struct {
30 | 	Success bool   `json:"success"`
31 | 	Message string `json:"message"`
32 | 	Result  []struct {
33 | 		MarketName     string  `json:"MarketName"`
34 | 		High           float64 `json:"High"`
35 | 		Low            float64 `json:"Low"`
36 | 		Last           float64 `json:"Last"`
37 | 		Volume         float64 `json:"Volume"`
38 | 		OpenSellOrders int     `json:"OpenSellOrders"`
39 | 		OpenBuyOrders  int     `json:"OpenBuyOrders"`
40 | 	} `json:"result"`
41 | }
42 | 
43 | func (list *summaryList) addSummaryItem(name, displayName string, marketList []*mCurrency) {
44 | 	list.items = append(list.items, &bCurrency{
45 | 		name:        name,
46 | 		displayName: displayName,
47 | 		markets:     marketList,
48 | 	})
49 | }
50 | 


--------------------------------------------------------------------------------
/modules/cryptocurrency/blockfolio/settings.go:
--------------------------------------------------------------------------------
 1 | package blockfolio
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Blockfolio"
11 | )
12 | 
13 | type colors struct {
14 | 	name  string
15 | 	grows string
16 | 	drop  string
17 | }
18 | 
19 | type Settings struct {
20 | 	*cfg.Common
21 | 
22 | 	colors
23 | 
24 | 	deviceToken     string
25 | 	displayHoldings bool
26 | }
27 | 
28 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
29 | 
30 | 	settings := Settings{
31 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
32 | 
33 | 		deviceToken:     ymlConfig.UString("device_token"),
34 | 		displayHoldings: ymlConfig.UBool("displayHoldings", true),
35 | 	}
36 | 
37 | 	settings.SetDocumentationPath("cryptocurrencies/blockfolio")
38 | 
39 | 	return &settings
40 | }
41 | 


--------------------------------------------------------------------------------
/modules/cryptocurrency/cryptolive/price/price.go:
--------------------------------------------------------------------------------
 1 | package price
 2 | 
 3 | type list struct {
 4 | 	items []*fromCurrency
 5 | }
 6 | 
 7 | type fromCurrency struct {
 8 | 	name        string
 9 | 	displayName string
10 | 	to          []*toCurrency
11 | }
12 | 
13 | type toCurrency struct {
14 | 	name  string
15 | 	price float32
16 | }
17 | 
18 | type cResponse map[string]float32
19 | 
20 | /* -------------------- Unexported Functions -------------------- */
21 | 
22 | func (l *list) addItem(name string, displayName string, to []*toCurrency) {
23 | 	l.items = append(l.items, &fromCurrency{
24 | 		name:        name,
25 | 		displayName: displayName,
26 | 		to:          to,
27 | 	})
28 | }
29 | 


--------------------------------------------------------------------------------
/modules/cryptocurrency/cryptolive/toplist/toplist.go:
--------------------------------------------------------------------------------
 1 | package toplist
 2 | 
 3 | type cList struct {
 4 | 	items []*fCurrency
 5 | }
 6 | 
 7 | type fCurrency struct {
 8 | 	name, displayName string
 9 | 	limit             int
10 | 	to                []*tCurrency
11 | }
12 | 
13 | type tCurrency struct {
14 | 	name string
15 | 	info []tInfo
16 | }
17 | 
18 | type tInfo struct {
19 | 	exchange               string
20 | 	volume24h, volume24hTo float32
21 | }
22 | 
23 | type responseInterface struct {
24 | 	Response string `json:"Response"`
25 | 	Data     []struct {
26 | 		Exchange    string  `json:"exchange"`
27 | 		FromSymbol  string  `json:"fromSymbol"`
28 | 		ToSymbol    string  `json:"toSymbol"`
29 | 		Volume24h   float32 `json:"volume24h"`
30 | 		Volume24hTo float32 `json:"volume24hTo"`
31 | 	} `json:"Data"`
32 | }
33 | 
34 | func (list *cList) addItem(name, displayName string, limit int, to []*tCurrency) {
35 | 	list.items = append(list.items, &fCurrency{
36 | 		name:        name,
37 | 		displayName: displayName,
38 | 		limit:       limit,
39 | 		to:          to,
40 | 	})
41 | }
42 | 


--------------------------------------------------------------------------------
/modules/cryptocurrency/mempool/settings.go:
--------------------------------------------------------------------------------
 1 | package mempool
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "mempool"
11 | )
12 | 
13 | // Settings defines the configuration properties for this module
14 | type Settings struct {
15 | 	common *cfg.Common
16 | 
17 | 	// Define your settings attributes here
18 | }
19 | 
20 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
21 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
22 | 	settings := Settings{
23 | 		common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
24 | 
25 | 		// Configure your settings attributes here. See http://github.com/olebedev/config for type details
26 | 	}
27 | 
28 | 	return &settings
29 | }
30 | 


--------------------------------------------------------------------------------
/modules/datadog/client.go:
--------------------------------------------------------------------------------
 1 | package datadog
 2 | 
 3 | import (
 4 | 	"github.com/wtfutil/wtf/utils"
 5 | 	datadog "github.com/zorkian/go-datadog-api"
 6 | )
 7 | 
 8 | // Monitors returns a list of Datadog monitors
 9 | func (widget *Widget) Monitors() ([]datadog.Monitor, error) {
10 | 	client := datadog.NewClient(
11 | 		widget.settings.apiKey,
12 | 		widget.settings.applicationKey,
13 | 	)
14 | 
15 | 	tags := utils.ToStrs(widget.settings.tags)
16 | 
17 | 	monitors, err := client.GetMonitorsByTags(tags)
18 | 	if err != nil {
19 | 		return nil, err
20 | 	}
21 | 
22 | 	return monitors, nil
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/datadog/keyboard.go:
--------------------------------------------------------------------------------
 1 | package datadog
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
13 | 	widget.SetKeyboardChar("o", widget.openItem, "Open item in browser")
14 | 
15 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
16 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
17 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
18 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openItem, "Open item in browser")
19 | }
20 | 


--------------------------------------------------------------------------------
/modules/datadog/settings.go:
--------------------------------------------------------------------------------
 1 | package datadog
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "DataDog"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiKey         string        `help:"Your Datadog API key."`
19 | 	applicationKey string        `help:"Your Datadog Application key."`
20 | 	tags           []interface{} `help:"Array of tags you want to query monitors by."`
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		apiKey:         ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_DATADOG_API_KEY"))),
29 | 		applicationKey: ymlConfig.UString("applicationKey", os.Getenv("WTF_DATADOG_APPLICATION_KEY")),
30 | 		tags:           ymlConfig.UList("monitors.tags"),
31 | 	}
32 | 
33 | 	cfg.ModuleSecret(name+"-api", globalConfig, &settings.apiKey).Load()
34 | 	cfg.ModuleSecret(name+"-app", globalConfig, &settings.applicationKey).Load()
35 | 
36 | 	return &settings
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/devto/keyboard.go:
--------------------------------------------------------------------------------
 1 | package devto
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("d", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("a", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openStory, "Open story in browser")
12 | 
13 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
14 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
15 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openStory, "Open story in browser")
16 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
17 | }
18 | 


--------------------------------------------------------------------------------
/modules/devto/settings.go:
--------------------------------------------------------------------------------
 1 | package devto
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 
 6 | 	"github.com/wtfutil/wtf/cfg"
 7 | )
 8 | 
 9 | const (
10 | 	defaultFocusable = true
11 | 	defaultTitle     = "dev.to | News Feed"
12 | )
13 | 
14 | // Settings defines the configuration options for this module
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	numberOfArticles int    `help:"Number of stories to show. Default is 10" optional:"true"`
19 | 	contentTag       string `help:"List articles from a specific tag. Default is empty" optional:"true"`
20 | 	contentUsername  string `help:"List articles from a specific user. Default is empty" optional:"true"`
21 | 	contentState     string `help:"Order the feed by fresh/rising. Default is rising" optional:"true"`
22 | }
23 | 
24 | // NewSettingsFromYAML creates and returns an instance of Settings with configuration options populated
25 | func NewSettingsFromYAML(name string, yamlConfig *config.Config, globalConfig *config.Config) *Settings {
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, yamlConfig, globalConfig),
28 | 
29 | 		numberOfArticles: yamlConfig.UInt("numberOfArticles", 10),
30 | 		contentTag:       yamlConfig.UString("contentTag", ""),
31 | 		contentUsername:  yamlConfig.UString("contentUsername", ""),
32 | 		contentState:     yamlConfig.UString("contentState", ""),
33 | 	}
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/digitalclock/display.go:
--------------------------------------------------------------------------------
 1 | package digitalclock
 2 | 
 3 | import "strings"
 4 | 
 5 | func mergeLines(outString []string) string {
 6 | 	return strings.Join(outString, "\n")
 7 | }
 8 | 
 9 | func renderWidget(widgetSettings Settings) string {
10 | 	var outputStrings []string
11 | 
12 | 	clockString, needBorder := renderClock(widgetSettings)
13 | 	if needBorder {
14 | 		outputStrings = append(outputStrings, mergeLines([]string{"", clockString, ""}))
15 | 	} else {
16 | 		outputStrings = append(outputStrings, clockString)
17 | 	}
18 | 
19 | 	if widgetSettings.withDate {
20 | 		outputStrings = append(outputStrings, getDate(widgetSettings.dateFormat, widgetSettings.withDatePrefix))
21 | 	}
22 | 
23 | 	if widgetSettings.withUTC {
24 | 		outputStrings = append(outputStrings, getUTC())
25 | 	}
26 | 
27 | 	if widgetSettings.withEpoch {
28 | 		outputStrings = append(outputStrings, getEpoch())
29 | 	}
30 | 
31 | 	return mergeLines(outputStrings)
32 | }
33 | 
34 | func (widget *Widget) display() {
35 | 	widget.Redraw(func() (string, string, bool) {
36 | 		title := widget.CommonSettings().Title
37 | 		if widget.settings.dateTitle {
38 | 			title = getDate(widget.settings.dateFormat, false)
39 | 		}
40 | 		return title, renderWidget(*widget.settings), false
41 | 	})
42 | }
43 | 


--------------------------------------------------------------------------------
/modules/digitalclock/widget.go:
--------------------------------------------------------------------------------
 1 | package digitalclock
 2 | 
 3 | import (
 4 | 	"github.com/rivo/tview"
 5 | 	"github.com/wtfutil/wtf/view"
 6 | )
 7 | 
 8 | // Widget is a text widget struct to hold info about the current widget
 9 | type Widget struct {
10 | 	view.TextWidget
11 | 
12 | 	settings *Settings
13 | }
14 | 
15 | // NewWidget creates a new widget using settings
16 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
17 | 	widget := Widget{
18 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
19 | 
20 | 		settings: settings,
21 | 	}
22 | 	if settings.centerAlign {
23 | 		widget.View.SetTextAlign(tview.AlignCenter)
24 | 	}
25 | 
26 | 	return &widget
27 | }
28 | 
29 | /* -------------------- Exported Functions -------------------- */
30 | 
31 | // Refresh updates the onscreen contents of the widget
32 | func (widget *Widget) Refresh() {
33 | 	widget.display()
34 | }
35 | 


--------------------------------------------------------------------------------
/modules/digitalocean/droplet.go:
--------------------------------------------------------------------------------
 1 | package digitalocean
 2 | 
 3 | import (
 4 | 	"strings"
 5 | 
 6 | 	"github.com/digitalocean/godo"
 7 | 	"github.com/wtfutil/wtf/utils"
 8 | )
 9 | 
10 | // Droplet represents WTF's view of a DigitalOcean droplet
11 | type Droplet struct {
12 | 	godo.Droplet
13 | 
14 | 	Image  godo.Image
15 | 	Region godo.Region
16 | }
17 | 
18 | // NewDroplet creates and returns an instance of Droplet
19 | func NewDroplet(doDroplet godo.Droplet) *Droplet {
20 | 	return &Droplet{
21 | 		doDroplet,
22 | 		*doDroplet.Image,
23 | 		*doDroplet.Region,
24 | 	}
25 | }
26 | 
27 | /* -------------------- Exported Functions -------------------- */
28 | 
29 | // StringValueForProperty returns a string value for the given column
30 | func (drop *Droplet) StringValueForProperty(propName string) (string, error) {
31 | 	// Figure out if we should forward this property to a sub-object
32 | 	// Lets us support "Region.Name" column definitions
33 | 	split := strings.Split(propName, ".")
34 | 
35 | 	switch split[0] {
36 | 	case "Image":
37 | 		return utils.StringValueForProperty(drop.Image, split[1])
38 | 	case "Region":
39 | 		return utils.StringValueForProperty(drop.Region, split[1])
40 | 	default:
41 | 		return utils.StringValueForProperty(drop, propName)
42 | 	}
43 | }
44 | 


--------------------------------------------------------------------------------
/modules/digitalocean/keyboard.go:
--------------------------------------------------------------------------------
 1 | package digitalocean
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("?", widget.showInfo, "Show info about the selected droplet")
10 | 
11 | 	widget.SetKeyboardChar("b", widget.dropletRestart, "Reboot the selected droplet")
12 | 	widget.SetKeyboardChar("j", widget.Prev, "Select previous item")
13 | 	widget.SetKeyboardChar("k", widget.Next, "Select next item")
14 | 	widget.SetKeyboardChar("p", widget.dropletEnabledPrivateNetworking, "Enable private networking for the selected drople")
15 | 	widget.SetKeyboardChar("s", widget.dropletShutDown, "Shut down the selected droplet")
16 | 	widget.SetKeyboardChar("u", widget.Unselect, "Clear selection")
17 | 
18 | 	widget.SetKeyboardKey(tcell.KeyCtrlD, widget.dropletDestroy, "Destroy the selected droplet")
19 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
20 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.showInfo, "Show info about the selected droplet")
21 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
22 | }
23 | 


--------------------------------------------------------------------------------
/modules/docker/example-conf.yml:
--------------------------------------------------------------------------------
 1 | wtf:
 2 |   colors:
 3 |     # background: black
 4 |     # foreground: blue
 5 |     border:
 6 |       focusable: darkslateblue
 7 |       focused: orange
 8 |       normal: gray
 9 |     checked: yellow
10 |     highlight: 
11 |       fore: black
12 |       back: gray
13 |     rows:
14 |       even: yellow
15 |       odd: white
16 |   grid:
17 |     # How _wide_ the columns are, in terminal characters. In this case we have
18 |     # four columns, each of which are 35 characters wide.
19 |     # columns: [50, ]
20 |     # How _high_ the rows are, in terminal lines. In this case we have four rows
21 |     # that support ten line of text and one of four.
22 |     # rows: [50]
23 |   refreshInterval: 1
24 |   openFileUtil: "open"
25 |   mods:
26 |     docker:
27 |       type: docker
28 |       title: "💻"
29 |       enabled: true
30 |       position:
31 |         top: 0
32 |         left: 0
33 |         height: 3
34 |         width: 3
35 |       refreshInterval: 1
36 |       labelColor: lightblue
37 |   


--------------------------------------------------------------------------------
/modules/docker/settings.go:
--------------------------------------------------------------------------------
 1 | package docker
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "docker"
11 | )
12 | 
13 | // Settings defines the configuration options for this module
14 | type Settings struct {
15 | 	*cfg.Common
16 | 
17 | 	labelColor string
18 | }
19 | 
20 | // NewSettingsFromYAML creates and returns an instance of Settings with configuration options populated
21 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
22 | 	settings := Settings{
23 | 		Common:     cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
24 | 		labelColor: ymlConfig.UString("labelColor", "white"),
25 | 	}
26 | 
27 | 	return &settings
28 | }
29 | 


--------------------------------------------------------------------------------
/modules/docker/utils.go:
--------------------------------------------------------------------------------
 1 | package docker
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"math"
 6 | 	"reflect"
 7 | 	"strconv"
 8 | )
 9 | 
10 | func padSlice(padLeft bool, slice interface{}, getter func(i int) string, setter func(i int, newVal string)) {
11 | 	rv := reflect.ValueOf(slice)
12 | 	length := rv.Len()
13 | 	maxLen := 0
14 | 	for i := 0; i < length; i++ {
15 | 		val := getter(i)
16 | 		maxLen = int(math.Max(float64(len(val)), float64(maxLen)))
17 | 	}
18 | 
19 | 	sign := "-"
20 | 	if padLeft {
21 | 		sign = ""
22 | 	}
23 | 
24 | 	for i := 0; i < length; i++ {
25 | 		val := getter(i)
26 | 		val = fmt.Sprintf("%"+sign+strconv.Itoa(maxLen)+"s", val)
27 | 		setter(i, val)
28 | 	}
29 | }
30 | 


--------------------------------------------------------------------------------
/modules/feedreader/keyboard.go:
--------------------------------------------------------------------------------
 1 | package feedreader
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openStory, "Open story in browser")
12 | 	widget.SetKeyboardChar("t", widget.toggleDisplayText, "Toggle display between title, link and title+content")
13 | 
14 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
15 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
16 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openStory, "Open story in browser")
17 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
18 | }
19 | 


--------------------------------------------------------------------------------
/modules/football/client.go:
--------------------------------------------------------------------------------
 1 | package football
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"net/http"
 6 | )
 7 | 
 8 | var (
 9 | 	footballAPIUrl = "https://api.football-data.org/v2"
10 | )
11 | 
12 | type leagueInfo struct {
13 | 	id      int
14 | 	caption string
15 | }
16 | 
17 | type Client struct {
18 | 	apiKey string
19 | }
20 | 
21 | func NewClient(apiKey string) *Client {
22 | 	client := Client{
23 | 		apiKey: apiKey,
24 | 	}
25 | 
26 | 	return &client
27 | }
28 | 
29 | func (client *Client) footballRequest(path string, id int) (*http.Response, error) {
30 | 
31 | 	url := fmt.Sprintf("%s/competitions/%d/%s", footballAPIUrl, id, path)
32 | 	req, err := http.NewRequest("GET", url, http.NoBody)
33 | 	req.Header.Add("Accept", "application/json")
34 | 	req.Header.Add("Content-Type", "application/json")
35 | 	req.Header.Add("X-Auth-Token", client.apiKey)
36 | 	if err != nil {
37 | 		return nil, err
38 | 	}
39 | 	httpClient := &http.Client{}
40 | 	resp, err := httpClient.Do(req)
41 | 	if err != nil {
42 | 		return nil, err
43 | 	}
44 | 	defer func() { _ = resp.Body.Close() }()
45 | 
46 | 	return resp, nil
47 | }
48 | 


--------------------------------------------------------------------------------
/modules/football/types.go:
--------------------------------------------------------------------------------
 1 | package football
 2 | 
 3 | type Team struct {
 4 | 	Name string `json:"name"`
 5 | }
 6 | 
 7 | type LeagueStandings struct {
 8 | 	Standings []struct {
 9 | 		Table []Table `json:"table"`
10 | 	} `json:"standings"`
11 | }
12 | 
13 | type Table struct {
14 | 	Draw           int  `json:"draw"`
15 | 	GoalDifference int  `json:"goalDifference"`
16 | 	Lost           int  `json:"lost"`
17 | 	Won            int  `json:"won"`
18 | 	PlayedGames    int  `json:"playedGames"`
19 | 	Points         int  `json:"points"`
20 | 	Position       int  `json:"position"`
21 | 	Team           Team `json:"team"`
22 | }
23 | 
24 | type LeagueFixtuers struct {
25 | 	Matches []Matches `json:"matches"`
26 | }
27 | 
28 | type Matches struct {
29 | 	AwayTeam Team   `json:"awayTeam"`
30 | 	HomeTeam Team   `json:"homeTeam"`
31 | 	Score    Score  `json:"score"`
32 | 	Stage    string `json:"stage"`
33 | 	Status   string `json:"status"`
34 | 	Date     string `json:"utcDate"`
35 | }
36 | 
37 | type Score struct {
38 | 	FullTime ScoreByTime `json:"fullTime"`
39 | 	HalfTime ScoreByTime `json:"halfTime"`
40 | 	Winner   string      `json:"winner"`
41 | }
42 | 
43 | type ScoreByTime struct {
44 | 	AwayTeam int `json:"awayTeam"`
45 | 	HomeTeam int `json:"homeTeam"`
46 | }
47 | 


--------------------------------------------------------------------------------
/modules/football/util.go:
--------------------------------------------------------------------------------
 1 | package football
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"fmt"
 6 | 	"strings"
 7 | 	"time"
 8 | 
 9 | 	"github.com/olekukonko/tablewriter"
10 | )
11 | 
12 | func createTable(header []string, buf *bytes.Buffer) *tablewriter.Table {
13 | 
14 | 	table := tablewriter.NewWriter(buf)
15 | 	if len(header) != 0 {
16 | 		table.SetHeader(header)
17 | 	}
18 | 	table.SetBorder(false)
19 | 	table.SetCenterSeparator(" ")
20 | 	table.SetColumnSeparator(" ")
21 | 	table.SetRowSeparator(" ")
22 | 	table.SetAlignment(tablewriter.ALIGN_LEFT)
23 | 
24 | 	return table
25 | }
26 | 
27 | func parseDateString(d string) string {
28 | 
29 | 	return fmt.Sprintf("🕙 %s", strings.Replace(d, "T", " ", 1))
30 | }
31 | 
32 | func getDateString(offset int) string {
33 | 
34 | 	today := time.Now()
35 | 	return today.AddDate(0, 0, offset).Format("2006-01-02")
36 | 
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/gcal/widget.go:
--------------------------------------------------------------------------------
 1 | package gcal
 2 | 
 3 | import (
 4 | 	"github.com/rivo/tview"
 5 | 	"github.com/wtfutil/wtf/view"
 6 | )
 7 | 
 8 | type Widget struct {
 9 | 	view.TextWidget
10 | 
11 | 	calEvents []*CalEvent
12 | 	err       error
13 | 	settings  *Settings
14 | 	tviewApp  *tview.Application
15 | }
16 | 
17 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
18 | 	widget := Widget{
19 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
20 | 
21 | 		tviewApp: tviewApp,
22 | 		settings: settings,
23 | 	}
24 | 
25 | 	return &widget
26 | }
27 | 
28 | /* -------------------- Exported Functions -------------------- */
29 | 
30 | func (widget *Widget) Disable() {
31 | 	widget.TextWidget.Disable()
32 | }
33 | 
34 | func (widget *Widget) Refresh() {
35 | 	if isAuthenticated(widget.settings.email) {
36 | 		widget.fetchAndDisplayEvents()
37 | 		return
38 | 	}
39 | 
40 | 	widget.tviewApp.Suspend(widget.authenticate)
41 | 	widget.Refresh()
42 | }
43 | 
44 | /* -------------------- Unexported Functions -------------------- */
45 | 
46 | func (widget *Widget) fetchAndDisplayEvents() {
47 | 	calEvents, err := widget.Fetch()
48 | 	if err != nil {
49 | 		widget.err = err
50 | 		widget.calEvents = []*CalEvent{}
51 | 	} else {
52 | 		widget.err = nil
53 | 		widget.calEvents = calEvents
54 | 	}
55 | 
56 | 	widget.display()
57 | }
58 | 


--------------------------------------------------------------------------------
/modules/gerrit/keyboard.go:
--------------------------------------------------------------------------------
 1 | package gerrit
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("h", widget.prevProject, "Select previous project")
12 | 	widget.SetKeyboardChar("l", widget.nextProject, "Select next project")
13 | 	widget.SetKeyboardChar("j", widget.nextReview, "Select next review")
14 | 	widget.SetKeyboardChar("k", widget.prevReview, "Select previous review")
15 | 
16 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.prevProject, "Select previous project")
17 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.nextProject, "Select next project")
18 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.nextReview, "Select next review")
19 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.prevReview, "Select previous review")
20 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.unselect, "Clear selection")
21 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openReview, "Open review in browser")
22 | }
23 | 


--------------------------------------------------------------------------------
/modules/git/keyboard.go:
--------------------------------------------------------------------------------
 1 | package git
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next source")
10 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous source")
11 | 	widget.SetKeyboardChar("p", widget.Pull, "Pull repo")
12 | 	widget.SetKeyboardChar("c", widget.Checkout, "Checkout branch")
13 | 
14 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous source")
15 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next source")
16 | }
17 | 


--------------------------------------------------------------------------------
/modules/git/variables.go:
--------------------------------------------------------------------------------
1 | //go:build !windows
2 | 
3 | package git
4 | 
5 | const (
6 | 	__go_cmd = "git"
7 | )
8 | 


--------------------------------------------------------------------------------
/modules/git/variables_win.go:
--------------------------------------------------------------------------------
1 | //go:build windows
2 | 
3 | package git
4 | 
5 | const (
6 | 	__go_cmd = "git.exe"
7 | )
8 | 


--------------------------------------------------------------------------------
/modules/github/keyboard.go:
--------------------------------------------------------------------------------
 1 | package github
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
13 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next source")
14 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous source")
15 | 	widget.SetKeyboardChar("o", widget.openRepo, "Open item in browser")
16 | 	widget.SetKeyboardChar("p", widget.openPulls, "Open pull requests in browser")
17 | 	widget.SetKeyboardChar("i", widget.openIssues, "Open issues in browser")
18 | 
19 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
20 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
21 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next source")
22 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous source")
23 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openPr, "Open PR in browser")
24 | 	widget.SetKeyboardKey(tcell.KeyInsert, widget.openRepo, "Open item in browser")
25 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
26 | }
27 | 


--------------------------------------------------------------------------------
/modules/gitlab/keyboard.go:
--------------------------------------------------------------------------------
 1 | package gitlab
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
13 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next project")
14 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous project")
15 | 	widget.SetKeyboardChar("o", widget.openRepo, "Open item in browser")
16 | 	widget.SetKeyboardChar("p", widget.openPulls, "Open merge requests in browser")
17 | 	widget.SetKeyboardChar("i", widget.openIssues, "Open issues in browser")
18 | 
19 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
20 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
21 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next project")
22 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous project")
23 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openItemInBrowser, "Open item in browser")
24 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
25 | }
26 | 


--------------------------------------------------------------------------------
/modules/gitlabtodo/keyboard.go:
--------------------------------------------------------------------------------
 1 | package gitlabtodo
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openTodo, "Open todo in browser")
12 | 	widget.SetKeyboardChar("x", widget.markAsDone, "Mark todo as done")
13 | 
14 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
15 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
16 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openTodo, "Open todo in browser")
17 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
18 | }
19 | 


--------------------------------------------------------------------------------
/modules/gitlabtodo/settings.go:
--------------------------------------------------------------------------------
 1 | package gitlabtodo
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "GitLab Todos"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	numberOfTodos int    `help:"Defines number of stories to be displayed. Default is 10" optional:"true"`
19 | 	apiKey        string `help:"A GitLab personal access token. Requires at least api access."`
20 | 	domain        string `help:"Your GitLab corporate domain."`
21 | 	showProject   bool   `help:"Determines whether or not to show the project a given todo is for."`
22 | }
23 | 
24 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
28 | 
29 | 		numberOfTodos: ymlConfig.UInt("numberOfTodos", 10),
30 | 		apiKey:        ymlConfig.UString("apiKey", os.Getenv("WTF_GITLAB_TOKEN")),
31 | 		domain:        ymlConfig.UString("domain", "https://gitlab.com"),
32 | 		showProject:   ymlConfig.UBool("showProject", true),
33 | 	}
34 | 
35 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).
36 | 		Service(settings.domain).Load()
37 | 
38 | 	return &settings
39 | }
40 | 


--------------------------------------------------------------------------------
/modules/gitter/gitter.go:
--------------------------------------------------------------------------------
 1 | package gitter
 2 | 
 3 | import "time"
 4 | 
 5 | type Rooms struct {
 6 | 	Results []Room `json:"results"`
 7 | }
 8 | 
 9 | type Room struct {
10 | 	ID   string `json:"id"`
11 | 	Name string `json:"name"`
12 | 	URI  string `json:"uri"`
13 | }
14 | 
15 | type User struct {
16 | 	ID          string `json:"id"`
17 | 	Username    string `json:"username"`
18 | 	DisplayName string `json:"displayName"`
19 | }
20 | 
21 | type Message struct {
22 | 	ID     string    `json:"id"`
23 | 	Text   string    `json:"text"`
24 | 	HTML   string    `json:"html"`
25 | 	Sent   time.Time `json:"sent"`
26 | 	From   User      `json:"fromUser"`
27 | 	Unread bool      `json:"unread"`
28 | }
29 | 


--------------------------------------------------------------------------------
/modules/gitter/keyboard.go:
--------------------------------------------------------------------------------
 1 | package gitter
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 
12 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
13 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
14 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
15 | }
16 | 


--------------------------------------------------------------------------------
/modules/gitter/settings.go:
--------------------------------------------------------------------------------
 1 | package gitter
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "Gitter"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiToken         string `help:"Your Gitter Personal Access Token."`
19 | 	numberOfMessages int    `help:"Maximum number of (newest) messages to be displayed. Default is 10" optional:"true"`
20 | 	roomURI          string `help:"The room you want to display." values:"Example: wtfutil/Lobby"`
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		apiToken:         ymlConfig.UString("apiToken", os.Getenv("WTF_GITTER_API_TOKEN")),
29 | 		numberOfMessages: ymlConfig.UInt("numberOfMessages", 10),
30 | 		roomURI:          ymlConfig.UString("roomUri", "wtfutil/Lobby"),
31 | 	}
32 | 
33 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiToken).Load()
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/googleanalytics/settings.go:
--------------------------------------------------------------------------------
 1 | package googleanalytics
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Google Analytics"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	months         int
17 | 	secretFile     string `help:"Your Google client secret JSON file." values:"A string representing a file path to the JSON secret file."`
18 | 	viewIds        map[string]interface{}
19 | 	enableRealtime bool
20 | }
21 | 
22 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
23 | 
24 | 	settings := Settings{
25 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
26 | 
27 | 		months:         ymlConfig.UInt("months"),
28 | 		secretFile:     ymlConfig.UString("secretFile"),
29 | 		viewIds:        ymlConfig.UMap("viewIds"),
30 | 		enableRealtime: ymlConfig.UBool("enableRealtime", false),
31 | 	}
32 | 
33 | 	settings.SetDocumentationPath("google/analytics")
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/googleanalytics/widget.go:
--------------------------------------------------------------------------------
 1 | package googleanalytics
 2 | 
 3 | import (
 4 | 	"github.com/rivo/tview"
 5 | 	"github.com/wtfutil/wtf/view"
 6 | )
 7 | 
 8 | type Widget struct {
 9 | 	view.TextWidget
10 | 
11 | 	settings *Settings
12 | }
13 | 
14 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
15 | 	widget := Widget{
16 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
17 | 
18 | 		settings: settings,
19 | 	}
20 | 
21 | 	return &widget
22 | }
23 | 
24 | func (widget *Widget) Refresh() {
25 | 	websiteReports := widget.fetch()
26 | 	contentTable := widget.createTable(websiteReports)
27 | 
28 | 	widget.Redraw(func() (string, string, bool) { return widget.CommonSettings().Title, contentTable, false })
29 | }
30 | 


--------------------------------------------------------------------------------
/modules/grafana/display.go:
--------------------------------------------------------------------------------
 1 | package grafana
 2 | 
 3 | import "fmt"
 4 | 
 5 | func (widget *Widget) content() (string, string, bool) {
 6 | 	title := widget.CommonSettings().Title
 7 | 
 8 | 	var out string
 9 | 	if widget.Err != nil {
10 | 		return title, widget.Err.Error(), false
11 | 	} else {
12 | 		for idx, alert := range widget.Alerts {
13 | 			out += fmt.Sprintf(` ["%d"][%s]%s - %s[""]`,
14 | 				idx,
15 | 				stateColor(alert.State),
16 | 				stateToEmoji(alert.State),
17 | 				alert.Name,
18 | 			)
19 | 			out += "\n"
20 | 		}
21 | 	}
22 | 
23 | 	return title, out, false
24 | }
25 | 
26 | func stateColor(state AlertState) string {
27 | 	switch state {
28 | 	case Ok:
29 | 		return "green"
30 | 	case Paused:
31 | 		return "yellow"
32 | 	case Alerting:
33 | 		return "red"
34 | 	case Pending:
35 | 		return "orange"
36 | 	case NoData:
37 | 		return "yellow"
38 | 	default:
39 | 		return "white"
40 | 	}
41 | }
42 | 
43 | func stateToEmoji(state AlertState) string {
44 | 	switch state {
45 | 	case Ok:
46 | 		return "✔"
47 | 	case Paused:
48 | 		return "⏸"
49 | 	case Alerting:
50 | 		return "✘"
51 | 	case Pending:
52 | 		return "?"
53 | 	case NoData:
54 | 		return "?"
55 | 	}
56 | 	return ""
57 | }
58 | 


--------------------------------------------------------------------------------
/modules/grafana/keyboard.go:
--------------------------------------------------------------------------------
 1 | package grafana
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous alert")
 7 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next alert")
 8 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openAlert, "Open alert in browser")
 9 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
10 | }
11 | 


--------------------------------------------------------------------------------
/modules/grafana/settings.go:
--------------------------------------------------------------------------------
 1 | package grafana
 2 | 
 3 | import (
 4 | 	"log"
 5 | 	"os"
 6 | 	"strings"
 7 | 
 8 | 	"github.com/olebedev/config"
 9 | 	"github.com/wtfutil/wtf/cfg"
10 | )
11 | 
12 | const (
13 | 	defaultFocusable = true
14 | 	defaultTitle     = "Grafana"
15 | )
16 | 
17 | type Settings struct {
18 | 	*cfg.Common
19 | 
20 | 	apiKey  string `help:"Your Grafana API token."`
21 | 	baseURI string `help:"Base url of your grafana instance"`
22 | }
23 | 
24 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
28 | 
29 | 		apiKey:  ymlConfig.UString("apiKey", os.Getenv("WTF_GRAFANA_API_KEY")),
30 | 		baseURI: ymlConfig.UString("baseUri", ""),
31 | 	}
32 | 
33 | 	if settings.baseURI == "" {
34 | 		log.Fatal("baseUri for grafana is empty, but is required")
35 | 	}
36 | 	settings.baseURI = strings.TrimSuffix(settings.baseURI, "/")
37 | 
38 | 	return &settings
39 | }
40 | 


--------------------------------------------------------------------------------
/modules/gspreadsheets/settings.go:
--------------------------------------------------------------------------------
 1 | package gspreadsheets
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Google Spreadsheets"
11 | )
12 | 
13 | type colors struct {
14 | 	values string
15 | }
16 | 
17 | type Settings struct {
18 | 	colors
19 | 	*cfg.Common
20 | 
21 | 	cellAddresses []interface{}
22 | 	cellNames     []interface{}
23 | 	secretFile    string
24 | 	sheetID       string
25 | }
26 | 
27 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
28 | 
29 | 	settings := Settings{
30 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
31 | 
32 | 		cellNames:  ymlConfig.UList("cells.names"),
33 | 		secretFile: ymlConfig.UString("secretFile"),
34 | 		sheetID:    ymlConfig.UString("sheetId"),
35 | 	}
36 | 
37 | 	settings.values = ymlConfig.UString("colors.values", "green")
38 | 
39 | 	settings.SetDocumentationPath("google/spreadsheet")
40 | 
41 | 	return &settings
42 | }
43 | 


--------------------------------------------------------------------------------
/modules/hackernews/keyboard.go:
--------------------------------------------------------------------------------
 1 | package hackernews
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openStory, "Open story in browser")
12 | 	widget.SetKeyboardChar("c", widget.openComments, "Open comments in browser")
13 | 
14 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
15 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
16 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openStory, "Open story in browser")
17 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
18 | }
19 | 


--------------------------------------------------------------------------------
/modules/hackernews/settings.go:
--------------------------------------------------------------------------------
 1 | package hackernews
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "HackerNews"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	numberOfStories int    `help:"Defines number of stories to be displayed. Default is 10" optional:"true"`
17 | 	storyType       string `help:"Category of story to see" values:"new, top, job, ask" optional:"true"`
18 | }
19 | 
20 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
21 | 
22 | 	settings := Settings{
23 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
24 | 
25 | 		numberOfStories: ymlConfig.UInt("numberOfStories", 10),
26 | 		storyType:       ymlConfig.UString("storyType", "top"),
27 | 	}
28 | 
29 | 	return &settings
30 | }
31 | 


--------------------------------------------------------------------------------
/modules/hackernews/story.go:
--------------------------------------------------------------------------------
 1 | package hackernews
 2 | 
 3 | import "fmt"
 4 | 
 5 | const (
 6 | 	hnStoryPath = "https://news.ycombinator.com/item?id="
 7 | )
 8 | 
 9 | // Story represents a story submission on HackerNews
10 | type Story struct {
11 | 	By          string `json:"by"`
12 | 	Descendants int    `json:"descendants"`
13 | 	ID          int    `json:"id"`
14 | 	Kids        []int  `json:"kids"`
15 | 	Score       int    `json:"score"`
16 | 	Time        int    `json:"time"`
17 | 	Title       string `json:"title"`
18 | 	Type        string `json:"type"`
19 | 	URL         string `json:"url"`
20 | }
21 | 
22 | // CommentLink return the link to the HackerNews story comments page
23 | func (story *Story) CommentLink() string {
24 | 	return fmt.Sprintf("%s%d", hnStoryPath, story.ID)
25 | }
26 | 
27 | // Link returns the link to a story. If the story has an external link, that is returned
28 | // If the story has no external link, the HackerNews comments link is returned instead
29 | func (story *Story) Link() string {
30 | 	if story.URL != "" {
31 | 		return story.URL
32 | 	}
33 | 
34 | 	// Fall back to the HackerNews comment link
35 | 	return story.CommentLink()
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/hackernews/story_test.go:
--------------------------------------------------------------------------------
 1 | package hackernews
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"gotest.tools/assert"
 7 | )
 8 | 
 9 | func Test_CommentLink(t *testing.T) {
10 | 	story := Story{
11 | 		ID: 3,
12 | 	}
13 | 
14 | 	assert.Equal(t, "https://news.ycombinator.com/item?id=3", story.CommentLink())
15 | }
16 | 
17 | func Test_Link(t *testing.T) {
18 | 	tests := []struct {
19 | 		name     string
20 | 		id       int
21 | 		url      string
22 | 		expected string
23 | 	}{
24 | 		{
25 | 			name:     "no external link",
26 | 			id:       1,
27 | 			url:      "",
28 | 			expected: "https://news.ycombinator.com/item?id=1",
29 | 		},
30 | 		{
31 | 			name:     "with external link",
32 | 			id:       1,
33 | 			url:      "https://www.link.ca",
34 | 			expected: "https://www.link.ca",
35 | 		},
36 | 	}
37 | 
38 | 	for _, tt := range tests {
39 | 		t.Run(tt.name, func(t *testing.T) {
40 | 			story := Story{
41 | 				ID:  tt.id,
42 | 				URL: tt.url,
43 | 			}
44 | 
45 | 			actual := story.Link()
46 | 
47 | 			assert.Equal(t, tt.expected, actual)
48 | 		})
49 | 	}
50 | }
51 | 


--------------------------------------------------------------------------------
/modules/healthchecks/keyboard.go:
--------------------------------------------------------------------------------
1 | package healthchecks
2 | 
3 | func (widget *Widget) initializeKeyboardControls() {
4 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
5 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
6 | }
7 | 


--------------------------------------------------------------------------------
/modules/healthchecks/settings.go:
--------------------------------------------------------------------------------
 1 | package healthchecks
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | 	"github.com/wtfutil/wtf/utils"
 9 | )
10 | 
11 | const (
12 | 	defaultFocusable = true
13 | 	defaultTitle     = "Healthchecks.io"
14 | )
15 | 
16 | type Settings struct {
17 | 	*cfg.Common
18 | 
19 | 	apiKey string   `help:"An healthchecks API key." optional:"false"`
20 | 	apiURL string   `help:"Base URL for API" optional:"true"`
21 | 	tags   []string `help:"Filters the checks and returns only the checks that are tagged with the specified value"`
22 | }
23 | 
24 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
28 | 
29 | 		apiKey: ymlConfig.UString("apiKey", os.Getenv("WTF_HEALTHCHECKS_APIKEY")),
30 | 		apiURL: ymlConfig.UString("apiURL", "https://hc-ping.com/"),
31 | 		tags:   utils.ToStrs(ymlConfig.UList("tags")),
32 | 	}
33 | 
34 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).
35 | 		Service(settings.apiURL).Load()
36 | 
37 | 	return &settings
38 | }
39 | 


--------------------------------------------------------------------------------
/modules/hibp/hibp_breach.go:
--------------------------------------------------------------------------------
 1 | package hibp
 2 | 
 3 | import "time"
 4 | 
 5 | // Breach represents a breach in the HIBP system
 6 | type Breach struct {
 7 | 	Date string `json:"BreachDate"`
 8 | 	Name string `json:"Name"`
 9 | }
10 | 
11 | // BreachDate returns the date of the breach
12 | func (br *Breach) BreachDate() (time.Time, error) {
13 | 	dt, err := time.Parse("2006-01-02", br.Date)
14 | 	if err != nil {
15 | 		// I would much rather return (nil, err) err but that doesn't seem possible
16 | 		// Not sure what a better value would be
17 | 		return time.Now(), err
18 | 	}
19 | 
20 | 	return dt, nil
21 | }
22 | 


--------------------------------------------------------------------------------
/modules/hibp/hibp_status.go:
--------------------------------------------------------------------------------
 1 | package hibp
 2 | 
 3 | // Status represents the status of an account in the HIBP system
 4 | type Status struct {
 5 | 	Account  string
 6 | 	Breaches []Breach
 7 | }
 8 | 
 9 | // NewStatus creates and returns an instance of Status
10 | func NewStatus(acct string, breaches []Breach) *Status {
11 | 	stat := Status{
12 | 		Account:  acct,
13 | 		Breaches: breaches,
14 | 	}
15 | 
16 | 	return &stat
17 | }
18 | 
19 | // HasBeenCompromised returns TRUE if the specified account has any breaches associated
20 | // with it, FALSE if no breaches are associated with it
21 | func (stat *Status) HasBeenCompromised() bool {
22 | 	return stat.Len() > 0
23 | }
24 | 
25 | // Len returns the number of breaches found for the specified account
26 | func (stat *Status) Len() int {
27 | 	if stat == nil || stat.Breaches == nil {
28 | 		return 0
29 | 	}
30 | 
31 | 	return len(stat.Breaches)
32 | }
33 | 


--------------------------------------------------------------------------------
/modules/ipaddresses/ipapi/settings.go:
--------------------------------------------------------------------------------
 1 | package ipapi
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "IP API"
11 | )
12 | 
13 | type colors struct {
14 | 	name  string
15 | 	value string
16 | }
17 | 
18 | type Settings struct {
19 | 	colors
20 | 	*cfg.Common
21 | 	args []interface{} `help:"Defines what data to display and the order." values:"'ip', 'isp', 'as', 'asName', 'district', 'city', 'region', 'regionName', 'country', 'countryCode', 'continent', 'continentCode', 'coordinates', 'postalCode', 'currency', 'organization', 'timezone' and/or 'reverseDNS'"`
22 | }
23 | 
24 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		args: ymlConfig.UList("args"),
29 | 	}
30 | 
31 | 	settings.name = ymlConfig.UString("colors.name", "red")
32 | 	settings.value = ymlConfig.UString("colors.value", "white")
33 | 	settings.SetDocumentationPath("ipaddress/ipapi")
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/jenkins/job.go:
--------------------------------------------------------------------------------
1 | package jenkins
2 | 
3 | type Job struct {
4 | 	Name  string `json:"name"`
5 | 	Url   string `json:"url"`
6 | 	Color string `json:"color"`
7 | }
8 | 


--------------------------------------------------------------------------------
/modules/jenkins/keyboard.go:
--------------------------------------------------------------------------------
 1 | package jenkins
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openJob, "Open job in browser")
12 | 
13 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
14 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
15 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openJob, "Open job in browser")
16 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
17 | }
18 | 


--------------------------------------------------------------------------------
/modules/jenkins/view.go:
--------------------------------------------------------------------------------
 1 | package jenkins
 2 | 
 3 | type View struct {
 4 | 	Description          string `json:"description"`
 5 | 	Jobs                 []Job  `json:"jobs"`
 6 | 	ActiveConfigurations []Job  `json:"activeConfigurations"`
 7 | 	Name                 string `json:"name"`
 8 | 	Url                  string `json:"url"`
 9 | }
10 | 


--------------------------------------------------------------------------------
/modules/jira/issues.go:
--------------------------------------------------------------------------------
 1 | package jira
 2 | 
 3 | type Issue struct {
 4 | 	Expand string `json:"expand"`
 5 | 	ID     string `json:"id"`
 6 | 	Self   string `json:"self"`
 7 | 	Key    string `json:"key"`
 8 | 
 9 | 	IssueFields *IssueFields `json:"fields"`
10 | }
11 | 
12 | type IssueFields struct {
13 | 	Summary string `json:"summary"`
14 | 
15 | 	IssueType   *IssueType   `json:"issuetype"`
16 | 	IssueStatus *IssueStatus `json:"status"`
17 | }
18 | 
19 | type IssueType struct {
20 | 	Self        string `json:"self"`
21 | 	ID          string `json:"id"`
22 | 	Description string `json:"description"`
23 | 	IconURL     string `json:"iconUrl"`
24 | 	Name        string `json:"name"`
25 | 	Subtask     bool   `json:"subtask"`
26 | }
27 | 
28 | type IssueStatus struct {
29 | 	ISelf        string `json:"self"`
30 | 	IDescription string `json:"description"`
31 | 	IName        string `json:"name"`
32 | }
33 | 


--------------------------------------------------------------------------------
/modules/jira/keyboard.go:
--------------------------------------------------------------------------------
 1 | package jira
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
13 | 	widget.SetKeyboardChar("o", widget.openItem, "Open item in browser")
14 | 
15 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
16 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
17 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openItem, "Open item in browser")
18 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
19 | }
20 | 


--------------------------------------------------------------------------------
/modules/jira/search_result.go:
--------------------------------------------------------------------------------
1 | package jira
2 | 
3 | type SearchResult struct {
4 | 	StartAt    int     `json:"startAt"`
5 | 	MaxResults int     `json:"maxResults"`
6 | 	Total      int     `json:"total"`
7 | 	Issues     []Issue `json:"issues"`
8 | }
9 | 


--------------------------------------------------------------------------------
/modules/kubernetes/client.go:
--------------------------------------------------------------------------------
 1 | package kubernetes
 2 | 
 3 | import (
 4 | 	"k8s.io/client-go/kubernetes"
 5 | 	// Includes authentication modules for various Kubernetes providers
 6 | 	_ "k8s.io/client-go/plugin/pkg/client/auth"
 7 | 	"k8s.io/client-go/tools/clientcmd"
 8 | )
 9 | 
10 | type clientInstance struct {
11 | 	Client kubernetes.Interface
12 | }
13 | 
14 | // getInstance returns a Kubernetes interface for a clientset
15 | func (widget *Widget) getInstance() (*clientInstance, error) {
16 | 	var err error
17 | 
18 | 	widget.clientOnce.Do(func() {
19 | 		widget.client = &clientInstance{}
20 | 		widget.client.Client, err = widget.getKubeClient()
21 | 	})
22 | 
23 | 	return widget.client, err
24 | }
25 | 
26 | // getKubeClient returns a kubernetes clientset for the kubeconfig provided
27 | func (widget *Widget) getKubeClient() (kubernetes.Interface, error) {
28 | 	var overrides *clientcmd.ConfigOverrides
29 | 	if widget.context != "" {
30 | 		overrides = &clientcmd.ConfigOverrides{
31 | 			CurrentContext: widget.context,
32 | 		}
33 | 	}
34 | 
35 | 	config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
36 | 		&clientcmd.ClientConfigLoadingRules{ExplicitPath: widget.kubeconfig},
37 | 		overrides).ClientConfig()
38 | 
39 | 	if err != nil {
40 | 		return nil, err
41 | 	}
42 | 
43 | 	clientset, err := kubernetes.NewForConfig(config)
44 | 	if err != nil {
45 | 		return nil, err
46 | 	}
47 | 	return clientset, nil
48 | }
49 | 


--------------------------------------------------------------------------------
/modules/kubernetes/settings.go:
--------------------------------------------------------------------------------
 1 | package kubernetes
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | 	"github.com/wtfutil/wtf/utils"
 7 | )
 8 | 
 9 | const (
10 | 	defaultFocusable = false
11 | 	defaultTitle     = "Kubernetes"
12 | )
13 | 
14 | type Settings struct {
15 | 	*cfg.Common
16 | 
17 | 	objects    []string `help:"Kubernetes objects to show. Options are: [nodes, pods, deployments]."`
18 | 	title      string   `help:"Override the title of widget."`
19 | 	kubeconfig string   `help:"Location of a kubeconfig file."`
20 | 	namespaces []string `help:"List of namespaces to watch. If blank, defaults to all namespaces."`
21 | 	context    string   `help:"Kubernetes context to use. If blank, uses default context"`
22 | }
23 | 
24 | func NewSettingsFromYAML(name string, moduleConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, moduleConfig, globalConfig),
28 | 
29 | 		objects:    utils.ToStrs(moduleConfig.UList("objects")),
30 | 		title:      moduleConfig.UString("title"),
31 | 		kubeconfig: moduleConfig.UString("kubeconfig"),
32 | 		namespaces: utils.ToStrs(moduleConfig.UList("namespaces")),
33 | 		context:    moduleConfig.UString("context"),
34 | 	}
35 | 
36 | 	return &settings
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/logger/settings.go:
--------------------------------------------------------------------------------
 1 | package logger
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "Logger"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	return &settings
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/lunarphase/keyboard.go:
--------------------------------------------------------------------------------
 1 | package lunarphase
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("n", widget.NextDay, "Show next day lunar phase")
10 | 	widget.SetKeyboardChar("p", widget.PrevDay, "Show previous day lunar phase")
11 | 	widget.SetKeyboardChar("t", widget.Today, "Show today lunar phase")
12 | 	widget.SetKeyboardChar("N", widget.NextWeek, "Show next week lunar phase")
13 | 	widget.SetKeyboardChar("P", widget.PrevWeek, "Show previous week lunar phase")
14 | 	widget.SetKeyboardChar("o", widget.OpenMoonPhase, "Open 'Moon Phase for Today' in browser")
15 | 
16 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevDay, "Show previous day lunar phase")
17 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextDay, "Show next day lunar phase")
18 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.NextWeek, "Show next week lunar phase")
19 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.PrevWeek, "Show previous week lunar phase")
20 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.OpenMoonPhase, "Open 'Moon Phase for Today' in browser")
21 | 	widget.SetKeyboardKey(tcell.KeyCtrlD, widget.DisableWidget, "Disable/Enable this widget instance")
22 | }
23 | 


--------------------------------------------------------------------------------
/modules/lunarphase/settings.go:
--------------------------------------------------------------------------------
 1 | package lunarphase
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "Phase of the Moon"
11 | 	dateFormat       = "2006-01-02"
12 | 	phaseFormat      = "01-02-2006"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	language       string
19 | 	requestTimeout int
20 | }
21 | 
22 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
23 | 	settings := Settings{
24 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
25 | 
26 | 		language:       ymlConfig.UString("language", "en"),
27 | 		requestTimeout: ymlConfig.UInt("timeout", 30),
28 | 	}
29 | 
30 | 	settings.SetDocumentationPath("lunarphase")
31 | 
32 | 	return &settings
33 | }
34 | 


--------------------------------------------------------------------------------
/modules/mercurial/keyboard.go:
--------------------------------------------------------------------------------
 1 | package mercurial
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next source")
10 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous source")
11 | 	widget.SetKeyboardChar("p", widget.Pull, "Pull repo")
12 | 	widget.SetKeyboardChar("c", widget.Checkout, "Checkout branch")
13 | 
14 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next source")
15 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous source")
16 | }
17 | 


--------------------------------------------------------------------------------
/modules/mercurial/settings.go:
--------------------------------------------------------------------------------
 1 | package mercurial
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "Mercurial"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	commitCount  int           `help:"The number of past commits to display." optional:"true"`
17 | 	commitFormat string        `help:"The string format for the commit message." optional:"true"`
18 | 	repositories []interface{} `help:"Defines which mercurial repositories to watch." values:"A list of zero or more local file paths pointing to valid mercurial repositories."`
19 | }
20 | 
21 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
22 | 
23 | 	settings := Settings{
24 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
25 | 
26 | 		commitCount:  ymlConfig.UInt("commitCount", 10),
27 | 		commitFormat: ymlConfig.UString("commitFormat", "[forestgreen]{rev}:{phase} [white]{desc|firstline|strip} [grey]{author|person} {date|age}[white]"),
28 | 		repositories: ymlConfig.UList("repositories"),
29 | 	}
30 | 
31 | 	return &settings
32 | }
33 | 


--------------------------------------------------------------------------------
/modules/nbascore/keyboard.go:
--------------------------------------------------------------------------------
 1 | package nbascore
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("l", widget.next, "Select next item")
10 | 	widget.SetKeyboardChar("h", widget.prev, "Select previous item")
11 | 	widget.SetKeyboardChar("c", widget.center, "Center on item")
12 | 
13 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.next, "Select next item")
14 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.prev, "Select previous item")
15 | }
16 | 
17 | func (widget *Widget) center() {
18 | 	offset = 0
19 | 	widget.Refresh()
20 | }
21 | 
22 | func (widget *Widget) next() {
23 | 	offset++
24 | 	widget.Refresh()
25 | }
26 | 
27 | func (widget *Widget) prev() {
28 | 	offset--
29 | 	widget.Refresh()
30 | }
31 | 


--------------------------------------------------------------------------------
/modules/nbascore/settings.go:
--------------------------------------------------------------------------------
 1 | package nbascore
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "NBA Score"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	settings.SetDocumentationPath("sports/nbascore")
23 | 
24 | 	return &settings
25 | }
26 | 


--------------------------------------------------------------------------------
/modules/newrelic/client.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	nr "github.com/wtfutil/wtf/modules/newrelic/client"
 5 | )
 6 | 
 7 | type Client2 struct {
 8 | 	applicationId int
 9 | 	nrClient      *nr.Client
10 | }
11 | 
12 | func NewClient(apiKey string, applicationId int) *Client2 {
13 | 	return &Client2{
14 | 		applicationId: applicationId,
15 | 		nrClient:      nr.NewClient(apiKey),
16 | 	}
17 | 
18 | }
19 | 
20 | func (client *Client2) Application() (*nr.Application, error) {
21 | 
22 | 	application, err := client.nrClient.GetApplication(client.applicationId)
23 | 	if err != nil {
24 | 		return nil, err
25 | 	}
26 | 
27 | 	return application, nil
28 | }
29 | 
30 | func (client *Client2) Deployments() ([]nr.ApplicationDeployment, error) {
31 | 
32 | 	opts := &nr.ApplicationDeploymentOptions{Page: 1}
33 | 	deployments, err := client.nrClient.GetApplicationDeployments(client.applicationId, opts)
34 | 	if err != nil {
35 | 		return nil, err
36 | 	}
37 | 
38 | 	return deployments, nil
39 | }
40 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/application_host_metrics.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | )
 6 | 
 7 | // GetApplicationHostMetrics will return a slice of Metric items for a
 8 | // particular Application ID's Host ID, optionally filtering by
 9 | // MetricsOptions.
10 | func (c *Client) GetApplicationHostMetrics(appID, hostID int, options *MetricsOptions) ([]Metric, error) {
11 | 	mc := NewMetricClient(c)
12 | 
13 | 	return mc.GetMetrics(
14 | 		fmt.Sprintf(
15 | 			"applications/%d/hosts/%d/metrics.json",
16 | 			appID,
17 | 			hostID,
18 | 		),
19 | 		options,
20 | 	)
21 | }
22 | 
23 | // GetApplicationHostMetricData will return all metric data for a particular
24 | // application's host and slice of metric names, optionally filtered by
25 | // MetricDataOptions.
26 | func (c *Client) GetApplicationHostMetricData(appID, hostID int, names []string, options *MetricDataOptions) (*MetricDataResponse, error) {
27 | 	mc := NewMetricClient(c)
28 | 
29 | 	return mc.GetMetricData(
30 | 		fmt.Sprintf(
31 | 			"applications/%d/hosts/%d/metrics/data.json",
32 | 			appID,
33 | 			hostID,
34 | 		),
35 | 		names,
36 | 		options,
37 | 	)
38 | }
39 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/application_instance_metrics.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | )
 6 | 
 7 | // GetApplicationInstanceMetrics will return a slice of Metric items for a
 8 | // particular Application ID's instance ID, optionally filtering by
 9 | // MetricsOptions.
10 | func (c *Client) GetApplicationInstanceMetrics(appID, instanceID int, options *MetricsOptions) ([]Metric, error) {
11 | 	mc := NewMetricClient(c)
12 | 
13 | 	return mc.GetMetrics(
14 | 		fmt.Sprintf(
15 | 			"applications/%d/instances/%d/metrics.json",
16 | 			appID,
17 | 			instanceID,
18 | 		),
19 | 		options,
20 | 	)
21 | }
22 | 
23 | // GetApplicationInstanceMetricData will return all metric data for a
24 | // particular application's instance and slice of metric names, optionally
25 | // filtered by MetricDataOptions.
26 | func (c *Client) GetApplicationInstanceMetricData(appID, instanceID int, names []string, options *MetricDataOptions) (*MetricDataResponse, error) {
27 | 	mc := NewMetricClient(c)
28 | 
29 | 	return mc.GetMetricData(
30 | 		fmt.Sprintf(
31 | 			"applications/%d/instances/%d/metrics/data.json",
32 | 			appID,
33 | 			instanceID,
34 | 		),
35 | 		names,
36 | 		options,
37 | 	)
38 | }
39 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/application_metrics.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | )
 6 | 
 7 | // GetApplicationMetrics will return a slice of Metric items for a
 8 | // particular Application ID, optionally filtering by
 9 | // MetricsOptions.
10 | func (c *Client) GetApplicationMetrics(id int, options *MetricsOptions) ([]Metric, error) {
11 | 	mc := NewMetricClient(c)
12 | 
13 | 	return mc.GetMetrics(
14 | 		fmt.Sprintf(
15 | 			"applications/%d/metrics.json",
16 | 			id,
17 | 		),
18 | 		options,
19 | 	)
20 | }
21 | 
22 | // GetApplicationMetricData will return all metric data for a particular
23 | // application and slice of metric names, optionally filtered by
24 | // MetricDataOptions.
25 | func (c *Client) GetApplicationMetricData(id int, names []string, options *MetricDataOptions) (*MetricDataResponse, error) {
26 | 	mc := NewMetricClient(c)
27 | 
28 | 	return mc.GetMetricData(
29 | 		fmt.Sprintf(
30 | 			"applications/%d/metrics/data.json",
31 | 			id,
32 | 		),
33 | 		names,
34 | 		options,
35 | 	)
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/array.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | // An Array is a type expected by the NewRelic API that differs from a comma-
 4 | // separated list. When passing GET params that expect an 'Array' type with
 5 | // one to many values, the expected format is "key=val1&key=val2" but an
 6 | // argument with zero to many values is of the form "key=val1,val2", and
 7 | // neither can be used in the other's place, so we have to differentiate
 8 | // somehow.
 9 | type Array struct {
10 | 	arr []string
11 | }
12 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/component_metrics.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | )
 6 | 
 7 | // GetComponentMetrics will return a slice of Metric items for a
 8 | // particular Component ID, optionally filtered by MetricsOptions.
 9 | func (c *Client) GetComponentMetrics(id int, options *MetricsOptions) ([]Metric, error) {
10 | 	mc := NewMetricClient(c)
11 | 
12 | 	return mc.GetMetrics(
13 | 		fmt.Sprintf(
14 | 			"components/%d/metrics.json",
15 | 			id,
16 | 		),
17 | 		options,
18 | 	)
19 | }
20 | 
21 | // GetComponentMetricData will return all metric data for a particular
22 | // component, optionally filtered by MetricDataOptions.
23 | func (c *Client) GetComponentMetricData(id int, names []string, options *MetricDataOptions) (*MetricDataResponse, error) {
24 | 	mc := NewMetricClient(c)
25 | 
26 | 	return mc.GetMetricData(
27 | 		fmt.Sprintf(
28 | 			"components/%d/metrics/data.json",
29 | 			id,
30 | 		),
31 | 		names,
32 | 		options,
33 | 	)
34 | }
35 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/main.go:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * NewRelic API for Go
 3 |  *
 4 |  * Please see the included LICENSE file for licensing information.
 5 |  *
 6 |  * Copyright 2016 by authors and contributors.
 7 |  */
 8 | 
 9 | package newrelic
10 | 
11 | import (
12 | 	"net/http"
13 | 	"net/url"
14 | 	"time"
15 | )
16 | 
17 | const (
18 | 	// defaultAPIURL is the default base URL for New Relic's latest API.
19 | 	defaultAPIURL = "https://api.newrelic.com/v2/"
20 | 	// defaultTimeout is the default timeout for the http.Client used.
21 | 	defaultTimeout = 5 * time.Second
22 | )
23 | 
24 | // Client provides a set of methods to interact with the New Relic API.
25 | type Client struct {
26 | 	apiKey     string
27 | 	httpClient *http.Client
28 | 	url        *url.URL
29 | }
30 | 
31 | // NewWithHTTPClient returns a new Client object for interfacing with the New
32 | // Relic API, allowing for override of the http.Client object.
33 | func NewWithHTTPClient(apiKey string, client *http.Client) *Client {
34 | 	u, err := url.Parse(defaultAPIURL)
35 | 	if err != nil {
36 | 		panic(err)
37 | 	}
38 | 	return &Client{
39 | 		apiKey:     apiKey,
40 | 		httpClient: client,
41 | 		url:        u,
42 | 	}
43 | }
44 | 
45 | // NewClient returns a new Client object for interfacing with the New Relic API.
46 | func NewClient(apiKey string) *Client {
47 | 	return NewWithHTTPClient(apiKey, &http.Client{Timeout: defaultTimeout})
48 | }
49 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/mobile_application_metrics.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | )
 6 | 
 7 | // GetMobileApplicationMetrics will return a slice of Metric items for a
 8 | // particular MobileAplication ID, optionally filtering by
 9 | // MetricsOptions.
10 | func (c *Client) GetMobileApplicationMetrics(id int, options *MetricsOptions) ([]Metric, error) {
11 | 	mc := NewMetricClient(c)
12 | 
13 | 	return mc.GetMetrics(
14 | 		fmt.Sprintf(
15 | 			"mobile_applications/%d/metrics.json",
16 | 			id,
17 | 		),
18 | 		options,
19 | 	)
20 | }
21 | 
22 | // GetMobileApplicationMetricData will return all metric data for a particular
23 | // MobileAplication and slice of metric names, optionally filtered by
24 | // MetricDataOptions.
25 | func (c *Client) GetMobileApplicationMetricData(id int, names []string, options *MetricDataOptions) (*MetricDataResponse, error) {
26 | 	mc := NewMetricClient(c)
27 | 
28 | 	return mc.GetMetricData(
29 | 		fmt.Sprintf(
30 | 			"mobile_applications/%d/metrics/data.json",
31 | 			id,
32 | 		),
33 | 		names,
34 | 		options,
35 | 	)
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/newrelic/client/server_metrics.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | )
 6 | 
 7 | // GetServerMetrics will return a slice of Metric items for a particular
 8 | // Server ID, optionally filtering by MetricsOptions.
 9 | func (c *Client) GetServerMetrics(id int, options *MetricsOptions) ([]Metric, error) {
10 | 	mc := NewMetricClient(c)
11 | 
12 | 	return mc.GetMetrics(
13 | 		fmt.Sprintf(
14 | 			"servers/%d/metrics.json",
15 | 			id,
16 | 		),
17 | 		options,
18 | 	)
19 | }
20 | 
21 | // GetServerMetricData will return all metric data for a particular Server and
22 | // slice of metric names, optionally filtered by MetricDataOptions.
23 | func (c *Client) GetServerMetricData(id int, names []string, options *MetricDataOptions) (*MetricDataResponse, error) {
24 | 	mc := NewMetricClient(c)
25 | 
26 | 	return mc.GetMetricData(
27 | 		fmt.Sprintf(
28 | 			"servers/%d/metrics/data.json",
29 | 			id,
30 | 		),
31 | 		names,
32 | 		options,
33 | 	)
34 | }
35 | 


--------------------------------------------------------------------------------
/modules/newrelic/keyboard.go:
--------------------------------------------------------------------------------
1 | package newrelic
2 | 
3 | import "github.com/gdamore/tcell/v2"
4 | 
5 | func (widget *Widget) initializeKeyboardControls() {
6 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous application")
7 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next application")
8 | }
9 | 


--------------------------------------------------------------------------------
/modules/newrelic/settings.go:
--------------------------------------------------------------------------------
 1 | package newrelic
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "NewRelic"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiKey         string        `help:"Your New Relic API token."`
19 | 	deployCount    int           `help:"The number of past deploys to display on screen." optional:"true"`
20 | 	applicationIDs []interface{} `help:"The integer ID of the New Relic application you wish to report on."`
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		apiKey:         ymlConfig.UString("apiKey", os.Getenv("WTF_NEW_RELIC_API_KEY")),
29 | 		deployCount:    ymlConfig.UInt("deployCount", 5),
30 | 		applicationIDs: ymlConfig.UList("applicationIDs"),
31 | 	}
32 | 
33 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/nextbus/settings.go:
--------------------------------------------------------------------------------
 1 | package nextbus
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "nextbus"
11 | )
12 | 
13 | // Settings defines the configuration properties for this module
14 | type Settings struct {
15 | 	common *cfg.Common
16 | 
17 | 	route  string `help:"Route Number of your bus"`
18 | 	agency string `help:"Transit agency of your bus"`
19 | 	stopID string `help:"Your bus stop number"`
20 | }
21 | 
22 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 	settings := Settings{
25 | 		common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
26 | 
27 | 		route:  ymlConfig.UString("route"),
28 | 		agency: ymlConfig.UString("agency"),
29 | 		stopID: ymlConfig.UString("stopID"),
30 | 	}
31 | 
32 | 	return &settings
33 | }
34 | 


--------------------------------------------------------------------------------
/modules/pagerduty/sort.go:
--------------------------------------------------------------------------------
 1 | package pagerduty
 2 | 
 3 | import "github.com/PagerDuty/go-pagerduty"
 4 | 
 5 | type ByEscalationLevel []pagerduty.OnCall
 6 | 
 7 | func (s ByEscalationLevel) Len() int      { return len(s) }
 8 | func (s ByEscalationLevel) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 9 | 
10 | func (s ByEscalationLevel) Less(i, j int) bool {
11 | 	return s[i].EscalationLevel < s[j].EscalationLevel
12 | }
13 | 


--------------------------------------------------------------------------------
/modules/pihole/keyboard.go:
--------------------------------------------------------------------------------
 1 | package pihole
 2 | 
 3 | func (widget *Widget) initializeKeyboardControls() {
 4 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 5 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 6 | 
 7 | 	widget.SetKeyboardChar("d", widget.disable, "disable Pi-hole")
 8 | 	widget.SetKeyboardChar("e", widget.enable, "enable Pi-hole")
 9 | }
10 | 


--------------------------------------------------------------------------------
/modules/pihole/settings.go:
--------------------------------------------------------------------------------
 1 | package pihole
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "Pi-hole"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	wrapText       bool
17 | 	apiUrl         string
18 | 	token          string
19 | 	showTopItems   int
20 | 	showTopClients int
21 | 	maxClientWidth int
22 | 	maxDomainWidth int
23 | 	showSummary    bool
24 | }
25 | 
26 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
27 | 	settings := Settings{
28 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
29 | 
30 | 		apiUrl:         ymlConfig.UString("apiUrl"),
31 | 		token:          ymlConfig.UString("token"),
32 | 		showSummary:    ymlConfig.UBool("showSummary", true),
33 | 		showTopItems:   ymlConfig.UInt("showTopItems", 5),
34 | 		showTopClients: ymlConfig.UInt("showTopClients", 5),
35 | 		maxClientWidth: ymlConfig.UInt("maxClientWidth", 20),
36 | 		maxDomainWidth: ymlConfig.UInt("maxDomainWidth", 20),
37 | 	}
38 | 
39 | 	cfg.ModuleSecret(name, globalConfig, &settings.token).
40 | 		Service(settings.apiUrl).Load()
41 | 
42 | 	return &settings
43 | }
44 | 


--------------------------------------------------------------------------------
/modules/pivotal/keyboard.go:
--------------------------------------------------------------------------------
 1 | package pivotal
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | )
 6 | 
 7 | func (widget *Widget) initializeKeyboardControls() {
 8 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 9 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
10 | 
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
13 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next source")
14 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous source")
15 | 	widget.SetKeyboardChar("o", widget.Open, "Open item in browser")
16 | 	widget.SetKeyboardChar("p", widget.OpenPulls, "Open pull requests in browser")
17 | 
18 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
19 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
20 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next source")
21 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous source")
22 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.Open, "Open PR in browser")
23 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
24 | }
25 | 


--------------------------------------------------------------------------------
/modules/pocket/item_service.go:
--------------------------------------------------------------------------------
 1 | package pocket
 2 | 
 3 | import "sort"
 4 | 
 5 | type sortByTimeAdded []Item
 6 | 
 7 | func (a sortByTimeAdded) Len() int           { return len(a) }
 8 | func (a sortByTimeAdded) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 9 | func (a sortByTimeAdded) Less(i, j int) bool { return a[i].TimeAdded > a[j].TimeAdded }
10 | 
11 | func orderItemResponseByKey(response ItemLists) []Item {
12 | 
13 | 	var items sortByTimeAdded
14 | 	for _, v := range response.List {
15 | 		items = append(items, v)
16 | 	}
17 | 	sort.Sort(items)
18 | 	return items
19 | }
20 | 


--------------------------------------------------------------------------------
/modules/pocket/keyboard.go:
--------------------------------------------------------------------------------
 1 | package pocket
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("a", widget.toggleLink, "Toggle Link")
10 | 	widget.SetKeyboardChar("t", widget.toggleView, "Toggle view (links ,archived links)")
11 | 	widget.SetKeyboardChar("j", widget.Next, "Select Next Link")
12 | 	widget.SetKeyboardChar("k", widget.Prev, "Select Previous Link")
13 | 	widget.SetKeyboardChar("o", widget.openLink, "Open Link in the browser")
14 | 
15 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select Next Link")
16 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select Previous Link")
17 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openLink, "Open Link in the browser")
18 | }
19 | 


--------------------------------------------------------------------------------
/modules/pocket/settings.go:
--------------------------------------------------------------------------------
 1 | package pocket
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "Pocket"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	consumerKey string
17 | 	requestKey  *string
18 | 	accessToken *string
19 | }
20 | 
21 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
22 | 	settings := Settings{
23 | 		Common:      cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
24 | 		consumerKey: ymlConfig.UString("consumerKey"),
25 | 	}
26 | 
27 | 	cfg.ModuleSecret(name, globalConfig, &settings.consumerKey).Load()
28 | 
29 | 	return &settings
30 | }
31 | 


--------------------------------------------------------------------------------
/modules/power/settings.go:
--------------------------------------------------------------------------------
 1 | package power
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Power"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	return &settings
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/power/source.go:
--------------------------------------------------------------------------------
 1 | //go:build !linux && !freebsd
 2 | 
 3 | package power
 4 | 
 5 | import (
 6 | 	"os/exec"
 7 | 	"regexp"
 8 | 	"strings"
 9 | 
10 | 	"github.com/wtfutil/wtf/utils"
11 | )
12 | 
13 | const SingleQuotesRegExp = "'(.*)'"
14 | 
15 | // powerSource returns the name of the current power source, probably one of
16 | // "AC Power" or "Battery Power"
17 | func powerSource() string {
18 | 	cmd := exec.Command("pmset", []string{"-g", "ps"}...)
19 | 	result := utils.ExecuteCommand(cmd)
20 | 
21 | 	r, _ := regexp.Compile(SingleQuotesRegExp)
22 | 
23 | 	source := r.FindString(result)
24 | 	source = strings.Replace(source, "'", "", -1)
25 | 
26 | 	return source
27 | }
28 | 


--------------------------------------------------------------------------------
/modules/power/source_freebsd.go:
--------------------------------------------------------------------------------
 1 | //go:build freebsd
 2 | 
 3 | package power
 4 | 
 5 | // powerSource returns the name of the current power source, probably one of
 6 | // "AC Power" or "Battery Power"
 7 | func powerSource() string {
 8 | 	switch batteryState {
 9 | 	case "1":
10 | 		return "AC Power"
11 | 	case "0":
12 | 		return "Battery Power"
13 | 	}
14 | 	return batteryState
15 | }
16 | 


--------------------------------------------------------------------------------
/modules/power/source_linux.go:
--------------------------------------------------------------------------------
 1 | //go:build linux
 2 | 
 3 | package power
 4 | 
 5 | // powerSource returns the name of the current power source, probably one of
 6 | // "AC Power" or "Battery Power"
 7 | func powerSource() string {
 8 | 	switch batteryState {
 9 | 	case "charging", "fully-charged":
10 | 		return "AC Power"
11 | 	case "discharging":
12 | 		return "Battery Power"
13 | 	}
14 | 	return batteryState
15 | }
16 | 


--------------------------------------------------------------------------------
/modules/resourceusage/settings.go:
--------------------------------------------------------------------------------
 1 | package resourceusage
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable       = false
10 | 	defaultRefreshInterval = "1s"
11 | 	defaultTitle           = "ResourceUsage"
12 | )
13 | 
14 | type Settings struct {
15 | 	*cfg.Common
16 | 
17 | 	cpuCombined bool
18 | 	showCPU     bool
19 | 	showMem     bool
20 | 	showSwp     bool
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 	settings := Settings{
25 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
26 | 
27 | 		cpuCombined: ymlConfig.UBool("cpuCombined", false),
28 | 		showCPU:     ymlConfig.UBool("showCPU", true),
29 | 		showMem:     ymlConfig.UBool("showMem", true),
30 | 		showSwp:     ymlConfig.UBool("showSwp", true),
31 | 	}
32 | 	settings.RefreshInterval = cfg.ParseTimeString(ymlConfig, "refreshInterval", defaultRefreshInterval)
33 | 
34 | 	return &settings
35 | }
36 | 


--------------------------------------------------------------------------------
/modules/rollbar/keyboard.go:
--------------------------------------------------------------------------------
 1 | package rollbar
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openBuild, "Open item in browser")
12 | 
13 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
14 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
15 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openBuild, "Open item in browser")
16 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
17 | }
18 | 


--------------------------------------------------------------------------------
/modules/rollbar/rollbar.go:
--------------------------------------------------------------------------------
 1 | package rollbar
 2 | 
 3 | type ActiveItems struct {
 4 | 	Results Result `json:"result"`
 5 | }
 6 | type Item struct {
 7 | 	Environment      string `json:"environment"`
 8 | 	Title            string `json:"title"`
 9 | 	Platform         string `json:"platform"`
10 | 	Status           string `json:"status"`
11 | 	TotalOccurrences int    `json:"total_occurrences"`
12 | 	Level            string `json:"level"`
13 | 	ID               int    `json:"counter"`
14 | }
15 | type Result struct {
16 | 	Items []Item `json:"items"`
17 | }
18 | 


--------------------------------------------------------------------------------
/modules/security/security_data.go:
--------------------------------------------------------------------------------
 1 | package security
 2 | 
 3 | type SecurityData struct {
 4 | 	Dns             []string
 5 | 	FirewallEnabled string
 6 | 	FirewallStealth string
 7 | 	LoggedInUsers   []string
 8 | 	WifiEncryption  string
 9 | 	WifiName        string
10 | }
11 | 
12 | func NewSecurityData() *SecurityData {
13 | 	return &SecurityData{}
14 | }
15 | 
16 | func (data SecurityData) DnsAt(idx int) string {
17 | 	if len(data.Dns) > idx {
18 | 		return data.Dns[idx]
19 | 	}
20 | 	return ""
21 | }
22 | 
23 | func (data *SecurityData) Fetch() {
24 | 	data.Dns = DnsServers()
25 | 	data.FirewallEnabled = FirewallState()
26 | 	data.FirewallStealth = FirewallStealthState()
27 | 	data.LoggedInUsers = LoggedInUsers()
28 | 	data.WifiName = WifiName()
29 | 	data.WifiEncryption = WifiEncryption()
30 | }
31 | 


--------------------------------------------------------------------------------
/modules/security/settings.go:
--------------------------------------------------------------------------------
 1 | package security
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Security"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	return &settings
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/spacex/client.go:
--------------------------------------------------------------------------------
 1 | package spacex
 2 | 
 3 | import (
 4 | 	"net/http"
 5 | 
 6 | 	"github.com/wtfutil/wtf/utils"
 7 | )
 8 | 
 9 | const (
10 | 	spacexLaunchAPI = "https://api.spacexdata.com/v3/launches/next"
11 | )
12 | 
13 | type Launch struct {
14 | 	FlightNumber int        `json:"flight_number"`
15 | 	MissionName  string     `json:"mission_name"`
16 | 	LaunchDate   int64      `json:"launch_date_unix"`
17 | 	IsTentative  bool       `json:"tentative"`
18 | 	Rocket       Rocket     `json:"rocket"`
19 | 	LaunchSite   LaunchSite `json:"launch_site"`
20 | 	Links        Links      `json:"links"`
21 | 	Details      string     `json:"details"`
22 | }
23 | 
24 | type LaunchSite struct {
25 | 	Name string `json:"site_name_long"`
26 | }
27 | 
28 | type Rocket struct {
29 | 	Name string `json:"rocket_name"`
30 | }
31 | 
32 | type Links struct {
33 | 	RedditLink  string `json:"reddit_campaign"`
34 | 	YouTubeLink string `json:"video_link"`
35 | }
36 | 
37 | func NextLaunch() (*Launch, error) {
38 | 	resp, err := http.Get(spacexLaunchAPI)
39 | 	if err != nil {
40 | 		return nil, err
41 | 	}
42 | 	defer func() { _ = resp.Body.Close() }()
43 | 
44 | 	var data Launch
45 | 	err = utils.ParseJSON(&data, resp.Body)
46 | 	if err != nil {
47 | 		return nil, err
48 | 	}
49 | 
50 | 	return &data, nil
51 | }
52 | 


--------------------------------------------------------------------------------
/modules/spacex/settings.go:
--------------------------------------------------------------------------------
 1 | package spacex
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | )
11 | 
12 | type Settings struct {
13 | 	*cfg.Common
14 | }
15 | 
16 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
17 | 	spacex := ymlConfig.UString("spacex")
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, spacex, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 	return &settings
22 | }
23 | 


--------------------------------------------------------------------------------
/modules/spotify/keyboard.go:
--------------------------------------------------------------------------------
 1 | package spotify
 2 | 
 3 | import (
 4 | 	"time"
 5 | 
 6 | 	"github.com/gdamore/tcell/v2"
 7 | )
 8 | 
 9 | func (widget *Widget) initializeKeyboardControls() {
10 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
11 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
12 | 
13 | 	widget.SetKeyboardChar("l", widget.next, "Select next item")
14 | 	widget.SetKeyboardChar("h", widget.previous, "Select previous item")
15 | 	widget.SetKeyboardChar(" ", widget.playPause, "Play/pause song")
16 | 
17 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.next, "Select next item")
18 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.previous, "Select previous item")
19 | }
20 | 
21 | func (widget *Widget) previous() {
22 | 	widget.client.Previous()
23 | 	time.Sleep(time.Second * 1)
24 | 	widget.Refresh()
25 | }
26 | 
27 | func (widget *Widget) next() {
28 | 	widget.client.Next()
29 | 	time.Sleep(time.Second * 1)
30 | 	widget.Refresh()
31 | }
32 | 
33 | func (widget *Widget) playPause() {
34 | 	widget.client.PlayPause()
35 | 	time.Sleep(time.Second * 1)
36 | 	widget.Refresh()
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/spotify/settings.go:
--------------------------------------------------------------------------------
 1 | package spotify
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "Spotify"
11 | )
12 | 
13 | type colors struct {
14 | 	label string
15 | 	text  string
16 | }
17 | 
18 | type Settings struct {
19 | 	colors
20 | 	*cfg.Common
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 	settings := Settings{
25 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
26 | 	}
27 | 
28 | 	settings.label = ymlConfig.UString("colors.label", "green")
29 | 	settings.text = ymlConfig.UString("colors.text", "white")
30 | 
31 | 	return &settings
32 | }
33 | 


--------------------------------------------------------------------------------
/modules/spotifyweb/settings.go:
--------------------------------------------------------------------------------
 1 | package spotifyweb
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "Spotify Web"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	callbackPort string
19 | 	clientID     string
20 | 	secretKey    string
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		callbackPort: ymlConfig.UString("callbackPort", "8080"),
29 | 		clientID:     ymlConfig.UString("clientID", os.Getenv("SPOTIFY_ID")),
30 | 		secretKey:    ymlConfig.UString("secretKey", os.Getenv("SPOTIFY_SECRET")),
31 | 	}
32 | 
33 | 	cfg.ModuleSecret(name, globalConfig, &settings.secretKey).Load()
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/status/settings.go:
--------------------------------------------------------------------------------
 1 | package status
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Status"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	return &settings
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/status/widget.go:
--------------------------------------------------------------------------------
 1 | package status
 2 | 
 3 | import (
 4 | 	"github.com/rivo/tview"
 5 | 	"github.com/wtfutil/wtf/view"
 6 | )
 7 | 
 8 | type Widget struct {
 9 | 	view.TextWidget
10 | 
11 | 	CurrentIcon int
12 | 
13 | 	settings *Settings
14 | }
15 | 
16 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
17 | 	widget := Widget{
18 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
19 | 
20 | 		CurrentIcon: 0,
21 | 
22 | 		settings: settings,
23 | 	}
24 | 
25 | 	return &widget
26 | }
27 | 
28 | /* -------------------- Exported Functions -------------------- */
29 | 
30 | func (widget *Widget) Refresh() {
31 | 	widget.Redraw(widget.animation)
32 | }
33 | 
34 | /* -------------------- Unexported Functions -------------------- */
35 | 
36 | func (widget *Widget) animation() (string, string, bool) {
37 | 	icons := []string{"|", "/", "-", "\\", "|"}
38 | 	next := icons[widget.CurrentIcon]
39 | 
40 | 	widget.CurrentIcon++
41 | 	if widget.CurrentIcon == len(icons) {
42 | 		widget.CurrentIcon = 0
43 | 	}
44 | 
45 | 	return widget.CommonSettings().Title, next, false
46 | }
47 | 


--------------------------------------------------------------------------------
/modules/steam/keyboard.go:
--------------------------------------------------------------------------------
 1 | package steam
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 
12 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
13 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
14 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
15 | }
16 | 


--------------------------------------------------------------------------------
/modules/steam/settings.go:
--------------------------------------------------------------------------------
 1 | package steam
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | 	"github.com/wtfutil/wtf/utils"
 9 | )
10 | 
11 | const (
12 | 	defaultFocusable = true
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	numberOfResults int      `help:"Number of rows to show. Default is 10." optional:"true"`
19 | 	key             string   `help:"Steam API key (default is env var STEAM_API_KEY)"`
20 | 	userIds         []string `help:"Steam user ids" optional:"true"`
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 	steam := ymlConfig.UString("steam")
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, steam, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		numberOfResults: ymlConfig.UInt("numberOfResults", 10),
29 | 		key:             ymlConfig.UString("key", os.Getenv("STEAM_API_KEY")),
30 | 		userIds:         utils.ToStrs(ymlConfig.UList("userIds", make([]interface{}, 0))),
31 | 	}
32 | 	return &settings
33 | }
34 | 


--------------------------------------------------------------------------------
/modules/stocks/finnhub/quote.go:
--------------------------------------------------------------------------------
 1 | package finnhub
 2 | 
 3 | type Quote struct {
 4 | 	C  float64 `json:"c"`
 5 | 	H  float64 `json:"h"`
 6 | 	L  float64 `json:"l"`
 7 | 	O  float64 `json:"o"`
 8 | 	Pc float64 `json:"pc"`
 9 | 	T  int     `json:"t"`
10 | 
11 | 	Stock string
12 | }
13 | 


--------------------------------------------------------------------------------
/modules/stocks/finnhub/settings.go:
--------------------------------------------------------------------------------
 1 | package finnhub
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | 	"github.com/wtfutil/wtf/utils"
 9 | )
10 | 
11 | const (
12 | 	defaultFocusable = true
13 | 	defaultTitle     = "📈 Stocks Price"
14 | )
15 | 
16 | // Settings defines the configuration properties for this module
17 | type Settings struct {
18 | 	*cfg.Common
19 | 
20 | 	apiKey  string   `help:"Your finnhub API token."`
21 | 	symbols []string `help:"An array of stocks symbols (i.e. AAPL, MSFT)"`
22 | }
23 | 
24 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
25 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
26 | 
27 | 	settings := Settings{
28 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
29 | 
30 | 		apiKey:  ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_FINNHUB_API_KEY"))),
31 | 		symbols: utils.ToStrs(ymlConfig.UList("symbols")),
32 | 	}
33 | 
34 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
35 | 
36 | 	return &settings
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/subreddit/keyboard.go:
--------------------------------------------------------------------------------
 1 | package subreddit
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openLink, "Open target URL in browser")
12 | 	widget.SetKeyboardChar("c", widget.openReddit, "Open Reddit comments in browser")
13 | 
14 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
15 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
16 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openReddit, "Open story in browser")
17 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
18 | }
19 | 


--------------------------------------------------------------------------------
/modules/subreddit/link.go:
--------------------------------------------------------------------------------
 1 | package subreddit
 2 | 
 3 | type Link struct {
 4 | 	Score     int    `json:"ups"`
 5 | 	Title     string `json:"title"`
 6 | 	ItemURL   string `json:"url"`
 7 | 	Permalink string `json:"permalink"`
 8 | }
 9 | 
10 | type RedditDocument struct {
11 | 	Data Subreddit `json:"data"`
12 | }
13 | 
14 | type RedditLinkDocument struct {
15 | 	Data Link `json:"data"`
16 | }
17 | 
18 | type Subreddit struct {
19 | 	Children []RedditLinkDocument `json:"Children"`
20 | }
21 | 


--------------------------------------------------------------------------------
/modules/subreddit/settings.go:
--------------------------------------------------------------------------------
 1 | package subreddit
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | )
11 | 
12 | // Settings contains the settings for the subreddit view
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	subreddit     string `help:"Subreddit to look at" optional:"false"`
17 | 	numberOfPosts int    `help:"Number of posts to show. Default is 10." optional:"true"`
18 | 	sortOrder     string `help:"Sort order for the posts (hot, new, rising, top), default hot" optional:"true"`
19 | 	topTimePeriod string `help:"If top sort is selected, the time period to show posts from (hour, week, day, month, year, all, default all)"`
20 | }
21 | 
22 | // NewSettingsFromYAML creates the settings for this module from a yaml file
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 	subreddit := ymlConfig.UString("subreddit")
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, subreddit, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		numberOfPosts: ymlConfig.UInt("numberOfPosts", 10),
29 | 		sortOrder:     ymlConfig.UString("sortOrder", "hot"),
30 | 		topTimePeriod: ymlConfig.UString("topTimePeriod", "all"),
31 | 		subreddit:     subreddit,
32 | 	}
33 | 
34 | 	return &settings
35 | }
36 | 


--------------------------------------------------------------------------------
/modules/system/settings.go:
--------------------------------------------------------------------------------
 1 | package system
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "System"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	return &settings
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/system/system_info.go:
--------------------------------------------------------------------------------
 1 | //go:build !windows
 2 | 
 3 | package system
 4 | 
 5 | import (
 6 | 	"os/exec"
 7 | 	"runtime"
 8 | 	"strings"
 9 | 
10 | 	"github.com/wtfutil/wtf/utils"
11 | )
12 | 
13 | type SystemInfo struct {
14 | 	ProductName    string
15 | 	ProductVersion string
16 | 	BuildVersion   string
17 | }
18 | 
19 | func NewSystemInfo() *SystemInfo {
20 | 	m := make(map[string]string)
21 | 
22 | 	arg := []string{}
23 | 
24 | 	var cmd *exec.Cmd
25 | 	switch runtime.GOOS {
26 | 	case "linux":
27 | 		arg = append(arg, "-a")
28 | 		cmd = exec.Command("lsb_release", arg...)
29 | 	case "darwin":
30 | 		cmd = exec.Command("sw_vers", arg...)
31 | 	default:
32 | 		cmd = exec.Command("sw_vers", arg...)
33 | 	}
34 | 
35 | 	raw := utils.ExecuteCommand(cmd)
36 | 
37 | 	for _, row := range strings.Split(raw, "\n") {
38 | 		parts := strings.Split(row, ":")
39 | 		if len(parts) < 2 {
40 | 			continue
41 | 		}
42 | 
43 | 		m[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
44 | 	}
45 | 
46 | 	var sysInfo *SystemInfo
47 | 	switch runtime.GOOS {
48 | 	case "linux":
49 | 		sysInfo = &SystemInfo{
50 | 			ProductName:    m["Distributor ID"],
51 | 			ProductVersion: m["Description"],
52 | 			BuildVersion:   m["Release"],
53 | 		}
54 | 	default:
55 | 		sysInfo = &SystemInfo{
56 | 			ProductName:    m["ProductName"],
57 | 			ProductVersion: m["ProductVersion"],
58 | 			BuildVersion:   m["BuildVersion"],
59 | 		}
60 | 
61 | 	}
62 | 	return sysInfo
63 | }
64 | 


--------------------------------------------------------------------------------
/modules/system/system_info_windows.go:
--------------------------------------------------------------------------------
 1 | //go:build windows
 2 | 
 3 | package system
 4 | 
 5 | import (
 6 | 	"os/exec"
 7 | 	"strings"
 8 | )
 9 | 
10 | type SystemInfo struct {
11 | 	ProductName    string
12 | 	ProductVersion string
13 | 	BuildVersion   string
14 | }
15 | 
16 | func NewSystemInfo() *SystemInfo {
17 | 	m := make(map[string]string)
18 | 
19 | 	cmd := exec.Command("powershell.exe", "(Get-CimInstance Win32_OperatingSystem).version")
20 | 	out, err := cmd.Output()
21 | 	if err != nil {
22 | 		panic(err)
23 | 	}
24 | 	s := strings.Split(string(out), ".")
25 | 	m["ProductName"] = "Windows"
26 | 	m["ProductVersion"] = "Windows " + s[0] + "." + s[1]
27 | 	m["BuildVersion"] = s[2]
28 | 
29 | 	sysInfo := SystemInfo{
30 | 		ProductName:    m["ProductName"],
31 | 		ProductVersion: m["ProductVersion"],
32 | 		BuildVersion:   m["BuildVersion"],
33 | 	}
34 | 
35 | 	return &sysInfo
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/system/widget.go:
--------------------------------------------------------------------------------
 1 | package system
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"time"
 6 | 
 7 | 	"github.com/rivo/tview"
 8 | 	"github.com/wtfutil/wtf/utils"
 9 | 	"github.com/wtfutil/wtf/view"
10 | )
11 | 
12 | type Widget struct {
13 | 	view.TextWidget
14 | 
15 | 	Date    string
16 | 	Version string
17 | 
18 | 	settings   *Settings
19 | 	systemInfo *SystemInfo
20 | }
21 | 
22 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, date, version string, settings *Settings) *Widget {
23 | 	widget := Widget{
24 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
25 | 
26 | 		Date: date,
27 | 
28 | 		settings: settings,
29 | 		Version:  version,
30 | 	}
31 | 
32 | 	widget.systemInfo = NewSystemInfo()
33 | 
34 | 	return &widget
35 | }
36 | 
37 | func (widget *Widget) display() (string, string, bool) {
38 | 	content := fmt.Sprintf(
39 | 		"%8s: %s\n%8s: %s\n\n%8s: %s\n%8s: %s",
40 | 		"Built",
41 | 		widget.prettyDate(),
42 | 		"Vers",
43 | 		widget.Version,
44 | 		"OS",
45 | 		widget.systemInfo.ProductVersion,
46 | 		"Build",
47 | 		widget.systemInfo.BuildVersion,
48 | 	)
49 | 
50 | 	return widget.CommonSettings().Title, content, false
51 | }
52 | 
53 | func (widget *Widget) Refresh() {
54 | 	widget.Redraw(widget.display)
55 | }
56 | 
57 | func (widget *Widget) prettyDate() string {
58 | 	str, err := time.Parse(utils.TimestampFormat, widget.Date)
59 | 
60 | 	if err != nil {
61 | 		return err.Error()
62 | 	}
63 | 
64 | 	return str.Format("Jan _2, 15:04")
65 | }
66 | 


--------------------------------------------------------------------------------
/modules/textfile/keyboard.go:
--------------------------------------------------------------------------------
 1 | package textfile
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | 	"github.com/wtfutil/wtf/utils"
 6 | )
 7 | 
 8 | func (widget *Widget) initializeKeyboardControls() {
 9 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
10 | 	widget.InitializeRefreshKeyboardControl(nil)
11 | 
12 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next file")
13 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous file")
14 | 	widget.SetKeyboardChar("o", widget.openFile, "Open file")
15 | 
16 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next file")
17 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous file")
18 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openFile, "Open file")
19 | }
20 | 
21 | func (widget *Widget) openFile() {
22 | 	src := widget.CurrentSource()
23 | 	utils.OpenFile(src)
24 | }
25 | 


--------------------------------------------------------------------------------
/modules/textfile/settings.go:
--------------------------------------------------------------------------------
 1 | package textfile
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = true
10 | 	defaultTitle     = "Textfile"
11 | )
12 | 
13 | // Settings defines the configuration properties for this module
14 | type Settings struct {
15 | 	*cfg.Common
16 | 
17 | 	filePaths   []interface{}
18 | 	format      bool
19 | 	formatStyle string
20 | 	wrapText    bool
21 | }
22 | 
23 | // NewSettingsFromYAML creates a new settings instance from a YAML config block
24 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
28 | 
29 | 		filePaths:   ymlConfig.UList("filePaths"),
30 | 		format:      ymlConfig.UBool("format", false),
31 | 		formatStyle: ymlConfig.UString("formatStyle", "vim"),
32 | 		wrapText:    ymlConfig.UBool("wrapText", true),
33 | 	}
34 | 
35 | 	return &settings
36 | }
37 | 


--------------------------------------------------------------------------------
/modules/todo_plus/backend/backend.go:
--------------------------------------------------------------------------------
 1 | package backend
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | )
 6 | 
 7 | type Backend interface {
 8 | 	Title() string
 9 | 	Setup(*config.Config)
10 | 	BuildProjects() []*Project
11 | 	GetProject(string) *Project
12 | 	LoadTasks(string) ([]Task, error)
13 | 	CloseTask(*Task) error
14 | 	DeleteTask(*Task) error
15 | 	Sources() []string
16 | }
17 | 


--------------------------------------------------------------------------------
/modules/todo_plus/backend/project.go:
--------------------------------------------------------------------------------
 1 | package backend
 2 | 
 3 | type Task struct {
 4 | 	ID        string
 5 | 	Completed bool
 6 | 	Name      string
 7 | }
 8 | 
 9 | type Project struct {
10 | 	ID   string
11 | 	Name string
12 | 
13 | 	Index   int
14 | 	Tasks   []Task
15 | 	Err     error
16 | 	backend Backend
17 | }
18 | 
19 | func (proj *Project) IsLast() bool {
20 | 	return proj.Index >= len(proj.Tasks)-1
21 | }
22 | 
23 | func (proj *Project) loadTasks() {
24 | 	Tasks, err := proj.backend.LoadTasks(proj.ID)
25 | 	proj.Err = err
26 | 	proj.Tasks = Tasks
27 | }
28 | 
29 | func (proj *Project) LongestLine() int {
30 | 	maxLen := 0
31 | 
32 | 	for _, task := range proj.Tasks {
33 | 		if len(task.Name) > maxLen {
34 | 			maxLen = len(task.Name)
35 | 		}
36 | 	}
37 | 
38 | 	return maxLen
39 | }
40 | 
41 | func (proj *Project) currentTask() *Task {
42 | 	if proj.Index < 0 {
43 | 		return nil
44 | 	}
45 | 
46 | 	return &proj.Tasks[proj.Index]
47 | }
48 | 
49 | func (proj *Project) CloseSelectedTask() {
50 | 	currTask := proj.currentTask()
51 | 
52 | 	if currTask != nil {
53 | 		_ = proj.backend.CloseTask(currTask)
54 | 		proj.loadTasks()
55 | 	}
56 | }
57 | 
58 | func (proj *Project) DeleteSelectedTask() {
59 | 	currTask := proj.currentTask()
60 | 
61 | 	if currTask != nil {
62 | 		_ = proj.backend.DeleteTask(currTask)
63 | 
64 | 		proj.loadTasks()
65 | 	}
66 | }
67 | 


--------------------------------------------------------------------------------
/modules/todo_plus/display.go:
--------------------------------------------------------------------------------
 1 | package todo_plus
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 
 6 | 	"github.com/rivo/tview"
 7 | 	"github.com/wtfutil/wtf/utils"
 8 | )
 9 | 
10 | func (widget *Widget) content() (string, string, bool) {
11 | 	proj := widget.CurrentProject()
12 | 
13 | 	if proj == nil {
14 | 		return widget.CommonSettings().Title, "", false
15 | 	}
16 | 
17 | 	if proj.Err != nil {
18 | 		return widget.CommonSettings().Title, proj.Err.Error(), true
19 | 	}
20 | 
21 | 	title := fmt.Sprintf(
22 | 		"[%s]%s[white]",
23 | 		widget.settings.Colors.Title,
24 | 		proj.Name)
25 | 
26 | 	str := ""
27 | 
28 | 	for idx, item := range proj.Tasks {
29 | 		row := fmt.Sprintf(
30 | 			`[%s]| | %s[%s]`,
31 | 			widget.RowColor(idx),
32 | 			tview.Escape(item.Name),
33 | 			widget.RowColor(idx),
34 | 		)
35 | 
36 | 		str += utils.HighlightableHelper(widget.View, row, idx, len(item.Name))
37 | 	}
38 | 	return title, str, false
39 | }
40 | 
41 | func (widget *Widget) display() {
42 | 	widget.Redraw(widget.content)
43 | }
44 | 


--------------------------------------------------------------------------------
/modules/todo_plus/keyboard.go:
--------------------------------------------------------------------------------
 1 | package todo_plus
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("d", widget.Delete, "Delete item")
10 | 	widget.SetKeyboardChar("j", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("k", widget.Next, "Select next item")
12 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous project")
13 | 	widget.SetKeyboardChar("c", widget.Close, "Close item")
14 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next project")
15 | 	widget.SetKeyboardChar("u", widget.Unselect, "Clear selection")
16 | 
17 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
18 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
19 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
20 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous project")
21 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next project")
22 | }
23 | 


--------------------------------------------------------------------------------
/modules/transmission/keyboard.go:
--------------------------------------------------------------------------------
 1 | package transmission
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(nil)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Prev, "Select previous item")
10 | 	widget.SetKeyboardChar("k", widget.Next, "Select next item")
11 | 	widget.SetKeyboardChar("u", widget.Unselect, "Clear selection")
12 | 
13 | 	widget.SetKeyboardKey(tcell.KeyCtrlD, widget.deleteSelectedTorrent, "Delete the selected torrent")
14 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
15 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.pauseUnpauseTorrent, "Pause/unpause torrent")
16 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
17 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
18 | }
19 | 


--------------------------------------------------------------------------------
/modules/travisci/keyboard.go:
--------------------------------------------------------------------------------
 1 | package travisci
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openBuild, "Open item in browser")
12 | 
13 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
14 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
15 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
16 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openBuild, "Open item in browser")
17 | }
18 | 


--------------------------------------------------------------------------------
/modules/travisci/settings.go:
--------------------------------------------------------------------------------
 1 | package travisci
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "TravisCI"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiKey  string
19 | 	baseURL string `help:"Your TravisCI Enterprise API URL." optional:"true"`
20 | 	compact bool
21 | 	limit   string
22 | 	pro     bool
23 | 	sort_by string
24 | }
25 | 
26 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
27 | 	settings := Settings{
28 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
29 | 
30 | 		apiKey:  ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_TRAVIS_API_TOKEN"))),
31 | 		baseURL: ymlConfig.UString("baseURL", ymlConfig.UString("baseURL", os.Getenv("WTF_TRAVIS_BASE_URL"))),
32 | 		pro:     ymlConfig.UBool("pro", false),
33 | 		compact: ymlConfig.UBool("compact", false),
34 | 		limit:   ymlConfig.UString("limit", "10"),
35 | 		sort_by: ymlConfig.UString("sort_by", "id:desc"),
36 | 	}
37 | 
38 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).
39 | 		Service(settings.baseURL).Load()
40 | 
41 | 	return &settings
42 | }
43 | 


--------------------------------------------------------------------------------
/modules/travisci/travis.go:
--------------------------------------------------------------------------------
 1 | package travisci
 2 | 
 3 | type Builds struct {
 4 | 	Builds []Build `json:"builds"`
 5 | }
 6 | 
 7 | type Build struct {
 8 | 	ID         int        `json:"id"`
 9 | 	CreatedBy  Owner      `json:"created_by"`
10 | 	Branch     Branch     `json:"branch"`
11 | 	Number     string     `json:"number"`
12 | 	Repository Repository `json:"repository"`
13 | 	Commit     Commit     `json:"commit"`
14 | 	State      string     `json:"state"`
15 | }
16 | 
17 | type Owner struct {
18 | 	Login string `json:"login"`
19 | }
20 | 
21 | type Branch struct {
22 | 	Name string `json:"name"`
23 | }
24 | 
25 | type Repository struct {
26 | 	Name string `json:"name"`
27 | 	Slug string `json:"slug"`
28 | }
29 | 
30 | type Commit struct {
31 | 	Message string `json:"message"`
32 | }
33 | 


--------------------------------------------------------------------------------
/modules/twitch/keyboard.go:
--------------------------------------------------------------------------------
 1 | package twitch
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openTwitch, "Open target URL in browser")
12 | 	widget.SetKeyboardChar("s", widget.openStreamlink, "Open target stream via streamlink (github.com/streamlink/streamlink)")
13 | 
14 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
15 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
16 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openTwitch, "Open stream in browser")
17 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
18 | }
19 | 


--------------------------------------------------------------------------------
/modules/twitter/keyboard.go:
--------------------------------------------------------------------------------
 1 | package twitter
 2 | 
 3 | import (
 4 | 	"github.com/gdamore/tcell/v2"
 5 | 	"github.com/wtfutil/wtf/utils"
 6 | )
 7 | 
 8 | func (widget *Widget) initializeKeyboardControls() {
 9 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
10 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
11 | 
12 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next source")
13 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous source")
14 | 	widget.SetKeyboardChar("o", widget.openFile, "Open source")
15 | 
16 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next source")
17 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous source")
18 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openFile, "Open source")
19 | }
20 | 
21 | func (widget *Widget) openFile() {
22 | 	src := widget.currentSourceURI()
23 | 	utils.OpenFile(src)
24 | }
25 | 


--------------------------------------------------------------------------------
/modules/twitter/request.go:
--------------------------------------------------------------------------------
 1 | package twitter
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"net/http"
 6 | )
 7 | 
 8 | func Request(httpClient *http.Client, apiURL string) ([]byte, error) {
 9 | 	resp, err := httpClient.Get(apiURL)
10 | 	if err != nil {
11 | 		return nil, err
12 | 	}
13 | 	defer func() { _ = resp.Body.Close() }()
14 | 
15 | 	data, err := ParseBody(resp)
16 | 	if err != nil {
17 | 		return nil, err
18 | 	}
19 | 
20 | 	return data, err
21 | }
22 | 
23 | func ParseBody(resp *http.Response) ([]byte, error) {
24 | 	var buffer bytes.Buffer
25 | 	_, err := buffer.ReadFrom(resp.Body)
26 | 	if err != nil {
27 | 		return nil, err
28 | 	}
29 | 
30 | 	return buffer.Bytes(), nil
31 | }
32 | 


--------------------------------------------------------------------------------
/modules/twitter/settings.go:
--------------------------------------------------------------------------------
 1 | package twitter
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "Twitter"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	bearerToken    string
19 | 	consumerKey    string
20 | 	consumerSecret string
21 | 	count          int
22 | 	screenNames    []interface{}
23 | }
24 | 
25 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
26 | 	settings := Settings{
27 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
28 | 
29 | 		bearerToken:    ymlConfig.UString("bearerToken", os.Getenv("WTF_TWITTER_BEARER_TOKEN")),
30 | 		consumerKey:    ymlConfig.UString("consumerKey", os.Getenv("WTF_TWITTER_CONSUMER_KEY")),
31 | 		consumerSecret: ymlConfig.UString("consumerSecret", os.Getenv("WTF_TWITTER_CONSUMER_SECRET")),
32 | 		count:          ymlConfig.UInt("count", 5),
33 | 		screenNames:    ymlConfig.UList("screenName"),
34 | 	}
35 | 
36 | 	settings.SetDocumentationPath("twitter/tweets")
37 | 
38 | 	return &settings
39 | }
40 | 


--------------------------------------------------------------------------------
/modules/twitter/tweet.go:
--------------------------------------------------------------------------------
 1 | package twitter
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"time"
 6 | )
 7 | 
 8 | type Tweet struct {
 9 | 	User      User   `json:"user"`
10 | 	Text      string `json:"text"`
11 | 	CreatedAt string `json:"created_at"`
12 | }
13 | 
14 | func (tweet *Tweet) String() string {
15 | 	return fmt.Sprintf("Tweet: %s at %s by %s", tweet.Text, tweet.CreatedAt, tweet.User.ScreenName)
16 | }
17 | 
18 | /* -------------------- Exported Functions -------------------- */
19 | 
20 | func (tweet *Tweet) Username() string {
21 | 	return tweet.User.ScreenName
22 | }
23 | 
24 | func (tweet *Tweet) Created() time.Time {
25 | 	newTime, _ := time.Parse(time.RubyDate, tweet.CreatedAt)
26 | 	return newTime
27 | }
28 | 
29 | func (tweet *Tweet) PrettyCreatedAt() string {
30 | 	newTime := tweet.Created()
31 | 	return fmt.Sprint(newTime.Format("Jan 2, 2006"))
32 | }
33 | 


--------------------------------------------------------------------------------
/modules/twitter/user.go:
--------------------------------------------------------------------------------
1 | package twitter
2 | 
3 | // User is used as part of the Tweet struct to get user information
4 | type User struct {
5 | 	ScreenName string `json:"screen_name"`
6 | }
7 | 


--------------------------------------------------------------------------------
/modules/twitterstats/settings.go:
--------------------------------------------------------------------------------
 1 | package twitterstats
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "Twitter Stats"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	bearerToken    string
19 | 	consumerKey    string
20 | 	consumerSecret string
21 | 	screenNames    []interface{}
22 | }
23 | 
24 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		bearerToken:    ymlConfig.UString("bearerToken", os.Getenv("WTF_TWITTER_BEARER_TOKEN")),
29 | 		consumerKey:    ymlConfig.UString("consumerKey", os.Getenv("WTF_TWITTER_CONSUMER_KEY")),
30 | 		consumerSecret: ymlConfig.UString("consumerSecret", os.Getenv("WTF_TWITTER_CONSUMER_SECRET")),
31 | 
32 | 		screenNames: ymlConfig.UList("screenNames"),
33 | 	}
34 | 
35 | 	settings.SetDocumentationPath("twitter/stats")
36 | 
37 | 	return &settings
38 | }
39 | 


--------------------------------------------------------------------------------
/modules/twitterstats/widget.go:
--------------------------------------------------------------------------------
 1 | package twitterstats
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 
 6 | 	"github.com/rivo/tview"
 7 | 	"github.com/wtfutil/wtf/view"
 8 | )
 9 | 
10 | type Widget struct {
11 | 	view.TextWidget
12 | 
13 | 	client   *Client
14 | 	settings *Settings
15 | }
16 | 
17 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tview.Pages, settings *Settings) *Widget {
18 | 	widget := Widget{
19 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
20 | 
21 | 		client:   NewClient(settings),
22 | 		settings: settings,
23 | 	}
24 | 
25 | 	widget.View.SetBorderPadding(1, 1, 1, 1)
26 | 	widget.View.SetWrap(false)
27 | 	widget.View.SetWordWrap(true)
28 | 
29 | 	return &widget
30 | }
31 | 
32 | func (widget *Widget) Refresh() {
33 | 	widget.Redraw(widget.content)
34 | }
35 | 
36 | func (widget *Widget) content() (string, string, bool) {
37 | 	// Add header row
38 | 	str := fmt.Sprintf(
39 | 		"[%s]%-12s %10s %8s[white]\n",
40 | 		widget.settings.Colors.Subheading,
41 | 		"Username",
42 | 		"Followers",
43 | 		"Tweets",
44 | 	)
45 | 
46 | 	stats := widget.client.GetStats()
47 | 
48 | 	// Add rows for each of the followed usernames
49 | 	for i, username := range widget.client.screenNames {
50 | 		str += fmt.Sprintf(
51 | 			"%-12s %10d %8d\n",
52 | 			username,
53 | 			stats[i].FollowerCount,
54 | 			stats[i].TweetCount,
55 | 		)
56 | 	}
57 | 
58 | 	return "Twitter Stats", str, false
59 | }
60 | 


--------------------------------------------------------------------------------
/modules/unknown/settings.go:
--------------------------------------------------------------------------------
 1 | package unknown
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Unknown"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | }
16 | 
17 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
18 | 	settings := Settings{
19 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
20 | 	}
21 | 
22 | 	return &settings
23 | }
24 | 


--------------------------------------------------------------------------------
/modules/unknown/widget.go:
--------------------------------------------------------------------------------
 1 | package unknown
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 
 6 | 	"github.com/rivo/tview"
 7 | 	"github.com/wtfutil/wtf/view"
 8 | )
 9 | 
10 | type Widget struct {
11 | 	view.TextWidget
12 | 
13 | 	settings *Settings
14 | }
15 | 
16 | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
17 | 	widget := Widget{
18 | 		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
19 | 
20 | 		settings: settings,
21 | 	}
22 | 
23 | 	return &widget
24 | }
25 | 
26 | /* -------------------- Exported Functions -------------------- */
27 | 
28 | func (widget *Widget) Refresh() {
29 | 	content := fmt.Sprintf("Widget %s and/or type %s does not exist", widget.Name(), widget.CommonSettings().Type)
30 | 	widget.Redraw(func() (string, string, bool) { return widget.CommonSettings().Title, content, true })
31 | }
32 | 


--------------------------------------------------------------------------------
/modules/updown/keyboard.go:
--------------------------------------------------------------------------------
1 | package updown
2 | 
3 | func (widget *Widget) initializeKeyboardControls() {
4 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
5 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
6 | }
7 | 


--------------------------------------------------------------------------------
/modules/updown/settings.go:
--------------------------------------------------------------------------------
 1 | package updown
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | 	"github.com/wtfutil/wtf/utils"
 9 | )
10 | 
11 | const (
12 | 	defaultFocusable = true
13 | 	defaultTitle     = "Updown.io"
14 | )
15 | 
16 | type Settings struct {
17 | 	*cfg.Common
18 | 
19 | 	apiKey string   `help:"An Updown API key." optional:"false"`
20 | 	tokens []string `help:"Filters the checks and returns only the checks with the specified tokens"`
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		apiKey: ymlConfig.UString("apiKey", os.Getenv("WTF_UPDOWN_APIKEY")),
29 | 		tokens: utils.ToStrs(ymlConfig.UList("tokens")),
30 | 	}
31 | 
32 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
33 | 
34 | 	return &settings
35 | }
36 | 


--------------------------------------------------------------------------------
/modules/uptimerobot/keyboard.go:
--------------------------------------------------------------------------------
1 | package uptimerobot
2 | 
3 | func (widget *Widget) initializeKeyboardControls() {
4 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
5 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
6 | }
7 | 


--------------------------------------------------------------------------------
/modules/uptimerobot/settings.go:
--------------------------------------------------------------------------------
 1 | package uptimerobot
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "Uptime Robot"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiKey        string `help:"An UptimeRobot API key."`
19 | 	uptimePeriods string `help:"The periods over which to display uptime (in days, dash-separated)." optional:"true"`
20 | 	offlineFirst  bool   `help:"Display offline monitors at the top." optional:"true"`
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		apiKey:        ymlConfig.UString("apiKey", os.Getenv("WTF_UPTIMEROBOT_APIKEY")),
29 | 		uptimePeriods: ymlConfig.UString("uptimePeriods", "30"),
30 | 		offlineFirst:  ymlConfig.UBool("offlineFirst", false),
31 | 	}
32 | 
33 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).
34 | 		Service("https://api.uptimerobot.com").Load()
35 | 
36 | 	return &settings
37 | }
38 | 


--------------------------------------------------------------------------------
/modules/urlcheck/client.go:
--------------------------------------------------------------------------------
 1 | package urlcheck
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"errors"
 6 | 	"fmt"
 7 | 	"net/http"
 8 | 	"time"
 9 | 
10 | 	"github.com/wtfutil/wtf/logger"
11 | )
12 | 
13 | // Perform the requet of the header for a given URL
14 | func DoRequest(urlRequest string, timeout time.Duration, client *http.Client) (int, string) {
15 | 
16 | 	// Define a Context with the timeout for the request
17 | 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
18 | 	defer cancel()
19 | 
20 | 	// Request
21 | 	req, err := http.NewRequest(http.MethodHead, urlRequest, nil)
22 | 	if err != nil {
23 | 		logger.Log(fmt.Sprintf("[urlcheck] ERROR %s: %s", urlRequest, err.Error()))
24 | 		return InvalidResultCode, "New Request Error"
25 | 	}
26 | 	req = req.WithContext(ctx)
27 | 
28 | 	// Send the request
29 | 	res, err := client.Do(req)
30 | 	if err != nil {
31 | 		if errors.Is(err, context.DeadlineExceeded) {
32 | 			status := "Timeout"
33 | 			logger.Log(fmt.Sprintf("[urlcheck] %s: %s", urlRequest, status))
34 | 			return InvalidResultCode, status
35 | 		}
36 | 		logger.Log(fmt.Sprintf("[urlcheck] %s: %s", urlRequest, err.Error()))
37 | 		return InvalidResultCode, "Error"
38 | 	}
39 | 
40 | 	defer res.Body.Close()
41 | 
42 | 	return res.StatusCode, res.Status
43 | }
44 | 


--------------------------------------------------------------------------------
/modules/urlcheck/client_test.go:
--------------------------------------------------------------------------------
 1 | package urlcheck
 2 | 
 3 | import (
 4 | 	"net/http"
 5 | 	"net/http/httptest"
 6 | 	"testing"
 7 | 	"time"
 8 | 
 9 | 	"gotest.tools/assert"
10 | )
11 | 
12 | func TestTimeout(t *testing.T) {
13 | 
14 | 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15 | 		time.Sleep(time.Second * 1)
16 | 	}))
17 | 	defer ts.Close()
18 | 
19 | 	client := &http.Client{
20 | 		Timeout: time.Millisecond * 10,
21 | 	}
22 | 
23 | 	timeout := 1 * time.Microsecond
24 | 	statusCode, statusMsg := DoRequest(ts.URL, timeout, client)
25 | 
26 | 	assert.Equal(t, 999, statusCode)
27 | 	assert.Equal(t, "Timeout", statusMsg)
28 | 
29 | }
30 | 


--------------------------------------------------------------------------------
/modules/urlcheck/settings.go:
--------------------------------------------------------------------------------
 1 | package urlcheck
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "URLcheck"
11 | )
12 | 
13 | type Settings struct {
14 | 	Common *cfg.Common
15 | 
16 | 	requestTimeout int      `help:"Max Request duration in seconds"`
17 | 	urls           []string `help:"A list of URL to check"`
18 | }
19 | 
20 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
21 | 	settings := Settings{
22 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
23 | 
24 | 		requestTimeout: ymlConfig.UInt("timeout", 30),
25 | 	}
26 | 	settings.urls = cfg.ParseAsMapOrList(ymlConfig, "urls")
27 | 	return &settings
28 | }
29 | 


--------------------------------------------------------------------------------
/modules/urlcheck/urlResult.go:
--------------------------------------------------------------------------------
 1 | package urlcheck
 2 | 
 3 | import (
 4 | 	"net/url"
 5 | )
 6 | 
 7 | const InvalidResultCode = 999
 8 | 
 9 | // Collect useful properties of each given URL
10 | type urlResult struct {
11 | 	Url           string
12 | 	ResultCode    int
13 | 	ResultMessage string
14 | 	IsValid       bool
15 | }
16 | 
17 | // Create a UrlResult instance from an urls occurence in the settings
18 | func newUrlResult(urlString string) *urlResult {
19 | 
20 | 	uResult := urlResult{
21 | 		Url: urlString,
22 | 	}
23 | 
24 | 	_, err := url.ParseRequestURI(urlString)
25 | 	if err != nil {
26 | 		uResult.ResultMessage = err.Error()
27 | 		uResult.ResultCode = InvalidResultCode
28 | 		uResult.IsValid = false
29 | 		return &uResult
30 | 	}
31 | 
32 | 	uResult.IsValid = true
33 | 	return &uResult
34 | }
35 | 


--------------------------------------------------------------------------------
/modules/urlcheck/urlResult_test.go:
--------------------------------------------------------------------------------
 1 | package urlcheck
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func checkValid(t *testing.T, got *urlResult) {
10 | 	assert.True(t, got.IsValid)
11 | 	assert.Less(t, got.ResultCode, 500)
12 | 	assert.Len(t, got.ResultMessage, 0)
13 | }
14 | 
15 | func checkInvalid(t *testing.T, got *urlResult) {
16 | 	assert.False(t, got.IsValid)
17 | 	assert.GreaterOrEqual(t, got.ResultCode, 500)
18 | 	assert.Greater(t, len(got.ResultMessage), 0)
19 | }
20 | 
21 | func Test_newUrlResult(t *testing.T) {
22 | 	type args struct {
23 | 		urlString string
24 | 	}
25 | 	type checks func(t *testing.T, res *urlResult)
26 | 
27 | 	tests := []struct {
28 | 		name   string
29 | 		args   args
30 | 		checks checks
31 | 	}{
32 | 		{"good", args{"http://www.go.dev"}, checkValid},
33 | 		{"good_with_page", args{"https://go.dev/doc/install"}, checkValid},
34 | 		{"good_with_args", args{"https://mysite.com?var=1"}, checkValid},
35 | 		{"no_url", args{""}, checkInvalid},
36 | 		{"no_escape_chars", args{"http://not\nurl.com?var=1"}, checkInvalid},
37 | 		{"no_protocol", args{"go.dev"}, checkInvalid},
38 | 	}
39 | 	for _, tt := range tests {
40 | 		t.Run(tt.name, func(t *testing.T) {
41 | 			if tt.checks != nil {
42 | 				tt.checks(t, newUrlResult(tt.args.urlString))
43 | 			}
44 | 		})
45 | 	}
46 | }
47 | 


--------------------------------------------------------------------------------
/modules/victorops/oncallresponse.go:
--------------------------------------------------------------------------------
 1 | package victorops
 2 | 
 3 | // OnCallResponse object
 4 | type OnCallResponse struct {
 5 | 	TeamsOnCall []struct {
 6 | 		Team struct {
 7 | 			Name string `json:"name"`
 8 | 			Slug string `json:"slug"`
 9 | 		} `json:"team"`
10 | 		OnCallNow []struct {
11 | 			EscalationPolicy struct {
12 | 				Name string `json:"name"`
13 | 				Slug string `json:"slug"`
14 | 			} `json:"escalationPolicy"`
15 | 			Users []struct {
16 | 				OnCallUser struct {
17 | 					Username string `json:"username"`
18 | 				} `json:"onCalluser"`
19 | 			} `json:"users"`
20 | 		} `json:"oncallNow"`
21 | 	} `json:"teamsOnCall"`
22 | }
23 | 


--------------------------------------------------------------------------------
/modules/victorops/oncallteam.go:
--------------------------------------------------------------------------------
 1 | package victorops
 2 | 
 3 | // OnCallTeam object to make
 4 | // managing objects easier
 5 | type OnCallTeam struct {
 6 | 	Name   string
 7 | 	Slug   string
 8 | 	OnCall []OnCall
 9 | }
10 | 
11 | // OnCall object to handle
12 | // different on call policies
13 | type OnCall struct {
14 | 	Policy   string
15 | 	Userlist string
16 | }
17 | 


--------------------------------------------------------------------------------
/modules/victorops/settings.go:
--------------------------------------------------------------------------------
 1 | package victorops
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "VictorOps"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiID  string
19 | 	apiKey string
20 | 	team   string
21 | }
22 | 
23 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
24 | 	settings := Settings{
25 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
26 | 
27 | 		apiID:  ymlConfig.UString("apiID", os.Getenv("WTF_VICTOROPS_API_ID")),
28 | 		apiKey: ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_VICTOROPS_API_KEY"))),
29 | 		team:   ymlConfig.UString("team"),
30 | 	}
31 | 
32 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
33 | 
34 | 	return &settings
35 | }
36 | 


--------------------------------------------------------------------------------
/modules/weatherservices/arpansagovau/settings.go:
--------------------------------------------------------------------------------
 1 | package arpansagovau
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "ARPANSA UV Data"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	city string
17 | }
18 | 
19 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
20 | 	settings := Settings{
21 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
22 | 		city:   ymlConfig.UString("locationid"),
23 | 	}
24 | 
25 | 	settings.SetDocumentationPath("weather_services/arpansagovau")
26 | 
27 | 	return &settings
28 | }
29 | 


--------------------------------------------------------------------------------
/modules/weatherservices/prettyweather/settings.go:
--------------------------------------------------------------------------------
 1 | package prettyweather
 2 | 
 3 | import (
 4 | 	"github.com/olebedev/config"
 5 | 	"github.com/wtfutil/wtf/cfg"
 6 | )
 7 | 
 8 | const (
 9 | 	defaultFocusable = false
10 | 	defaultTitle     = "Pretty Weather"
11 | )
12 | 
13 | type Settings struct {
14 | 	*cfg.Common
15 | 
16 | 	city     string
17 | 	unit     string
18 | 	view     string
19 | 	language string
20 | }
21 | 
22 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
23 | 	settings := Settings{
24 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
25 | 
26 | 		city:     ymlConfig.UString("city", "Barcelona"),
27 | 		language: ymlConfig.UString("language", "en"),
28 | 		unit:     ymlConfig.UString("unit", "m"),
29 | 		view:     ymlConfig.UString("view", "0"),
30 | 	}
31 | 
32 | 	settings.SetDocumentationPath("weather_services/prettyweather")
33 | 
34 | 	return &settings
35 | }
36 | 


--------------------------------------------------------------------------------
/modules/weatherservices/weather/keyboard.go:
--------------------------------------------------------------------------------
 1 | package weather
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("h", widget.PrevSource, "Select previous city")
10 | 	widget.SetKeyboardChar("l", widget.NextSource, "Select next city")
11 | 
12 | 	widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous city")
13 | 	widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next city")
14 | }
15 | 


--------------------------------------------------------------------------------
/modules/weatherservices/weather/settings.go:
--------------------------------------------------------------------------------
 1 | package weather
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "Weather"
13 | )
14 | 
15 | type colors struct {
16 | 	current string
17 | }
18 | 
19 | type Settings struct {
20 | 	colors
21 | 	*cfg.Common
22 | 
23 | 	apiKey   string
24 | 	cityIDs  []interface{}
25 | 	language string
26 | 	tempUnit string
27 | 	useEmoji bool
28 | 	compact  bool
29 | }
30 | 
31 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
32 | 	settings := Settings{
33 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
34 | 
35 | 		apiKey:   ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_OWM_API_KEY"))),
36 | 		cityIDs:  ymlConfig.UList("cityids"),
37 | 		language: ymlConfig.UString("language", "EN"),
38 | 		tempUnit: ymlConfig.UString("tempUnit", "C"),
39 | 		useEmoji: ymlConfig.UBool("useEmoji", true),
40 | 		compact:  ymlConfig.UBool("compact", false),
41 | 	}
42 | 
43 | 	settings.SetDocumentationPath("weather_services/weather/")
44 | 
45 | 	settings.current = ymlConfig.UString("colors.current", "green")
46 | 
47 | 	return &settings
48 | }
49 | 


--------------------------------------------------------------------------------
/modules/zendesk/client.go:
--------------------------------------------------------------------------------
 1 | package zendesk
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"io"
 6 | 	"net/http"
 7 | )
 8 | 
 9 | type Resource struct {
10 | 	Response interface{}
11 | 	Raw      string
12 | }
13 | 
14 | func (widget *Widget) api(meth string) (*Resource, error) {
15 | 	trn := &http.Transport{}
16 | 
17 | 	client := &http.Client{
18 | 		Transport: trn,
19 | 	}
20 | 
21 | 	baseURL := fmt.Sprintf("https://%v.zendesk.com/api/v2", widget.settings.subdomain)
22 | 	URL := baseURL + "/tickets.json?sort_by=status"
23 | 
24 | 	req, err := http.NewRequest(meth, URL, http.NoBody)
25 | 	if err != nil {
26 | 		return nil, err
27 | 	}
28 | 
29 | 	req.Header.Add("Content-Type", "application/json")
30 | 
31 | 	apiUser := fmt.Sprintf("%v/token", widget.settings.username)
32 | 	req.SetBasicAuth(apiUser, widget.settings.apiKey)
33 | 
34 | 	resp, err := client.Do(req)
35 | 	if err != nil {
36 | 		return nil, err
37 | 	}
38 | 	defer func() { _ = resp.Body.Close() }()
39 | 
40 | 	data, err := io.ReadAll(resp.Body)
41 | 	if err != nil {
42 | 		return nil, err
43 | 	}
44 | 
45 | 	return &Resource{Response: &resp, Raw: string(data)}, nil
46 | }
47 | 


--------------------------------------------------------------------------------
/modules/zendesk/keyboard.go:
--------------------------------------------------------------------------------
 1 | package zendesk
 2 | 
 3 | import "github.com/gdamore/tcell/v2"
 4 | 
 5 | func (widget *Widget) initializeKeyboardControls() {
 6 | 	widget.InitializeHelpTextKeyboardControl(widget.ShowHelp)
 7 | 	widget.InitializeRefreshKeyboardControl(widget.Refresh)
 8 | 
 9 | 	widget.SetKeyboardChar("j", widget.Next, "Select next item")
10 | 	widget.SetKeyboardChar("k", widget.Prev, "Select previous item")
11 | 	widget.SetKeyboardChar("o", widget.openTicket, "Open item")
12 | 
13 | 	widget.SetKeyboardKey(tcell.KeyDown, widget.Next, "Select next item")
14 | 	widget.SetKeyboardKey(tcell.KeyUp, widget.Prev, "Select previous item")
15 | 	widget.SetKeyboardKey(tcell.KeyEsc, widget.Unselect, "Clear selection")
16 | 	widget.SetKeyboardKey(tcell.KeyEnter, widget.openTicket, "Open item")
17 | }
18 | 


--------------------------------------------------------------------------------
/modules/zendesk/settings.go:
--------------------------------------------------------------------------------
 1 | package zendesk
 2 | 
 3 | import (
 4 | 	"os"
 5 | 
 6 | 	"github.com/olebedev/config"
 7 | 	"github.com/wtfutil/wtf/cfg"
 8 | )
 9 | 
10 | const (
11 | 	defaultFocusable = true
12 | 	defaultTitle     = "Zendesk"
13 | )
14 | 
15 | type Settings struct {
16 | 	*cfg.Common
17 | 
18 | 	apiKey    string
19 | 	status    string
20 | 	subdomain string
21 | 	username  string
22 | }
23 | 
24 | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
25 | 	settings := Settings{
26 | 		Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
27 | 
28 | 		apiKey:    ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("ZENDESK_API"))),
29 | 		status:    ymlConfig.UString("status"),
30 | 		subdomain: ymlConfig.UString("subdomain", os.Getenv("ZENDESK_SUBDOMAIN")),
31 | 		username:  ymlConfig.UString("username"),
32 | 	}
33 | 
34 | 	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
35 | 
36 | 	return &settings
37 | }
38 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 |   "devDependencies": {
3 |     "all-contributors-cli": "^6.20.0"
4 |   }
5 | }
6 | 


--------------------------------------------------------------------------------
/scripts/check-uncommitted-vendor-files.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | set -euo pipefail
 4 | 
 5 | GOPROXY="https://proxy.golang.org,direct" GOSUMDB=off GO111MODULE=on go mod tidy
 6 | 
 7 | untracked_files=$(git ls-files --others --exclude-standard | wc -l)
 8 | 
 9 | diff_stat=$(git diff --shortstat)
10 | 
11 | if [[ "${untracked_files}" -ne 0 || -n "${diff_stat}" ]]; then
12 |   echo 'Untracked or diff in tracked vendor files found. Please run "go mod tidy" and commit the changes'
13 |   exit 1
14 | fi
15 | 


--------------------------------------------------------------------------------
/utils/colors.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import "fmt"
 4 | 
 5 | // ColorizePercent provides a standard way to colorize percentages for which
 6 | // large numbers are good (green) and small numbers are bad (red).
 7 | func ColorizePercent(percent float64) string {
 8 | 	var color string
 9 | 
10 | 	switch {
11 | 	case percent >= 70:
12 | 		color = "green"
13 | 	case percent >= 35:
14 | 		color = "yellow"
15 | 	case percent < 0:
16 | 		color = "grey"
17 | 	default:
18 | 		color = "red"
19 | 	}
20 | 
21 | 	return fmt.Sprintf("[%s]%v[%s]", color, percent, "white")
22 | }
23 | 


--------------------------------------------------------------------------------
/utils/colors_test.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func Test_ColorizePercent(t *testing.T) {
10 | 	tests := []struct {
11 | 		name     string
12 | 		percent  float64
13 | 		expected string
14 | 	}{
15 | 		{
16 | 			name:     "with high percent",
17 | 			percent:  70,
18 | 			expected: "[green]70[white]",
19 | 		},
20 | 		{
21 | 			name:     "with medium percent",
22 | 			percent:  35,
23 | 			expected: "[yellow]35[white]",
24 | 		},
25 | 		{
26 | 			name:     "with low percent",
27 | 			percent:  1,
28 | 			expected: "[red]1[white]",
29 | 		},
30 | 		{
31 | 			name:     "with negative percent",
32 | 			percent:  -5,
33 | 			expected: "[grey]-5[white]",
34 | 		},
35 | 	}
36 | 
37 | 	for _, tt := range tests {
38 | 		t.Run(tt.name, func(t *testing.T) {
39 | 			actual := ColorizePercent(tt.percent)
40 | 			assert.Equal(t, tt.expected, actual)
41 | 		})
42 | 	}
43 | }
44 | 


--------------------------------------------------------------------------------
/utils/email_addresses.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import (
 4 | 	"strings"
 5 | 
 6 | 	"golang.org/x/text/cases"
 7 | 	"golang.org/x/text/language"
 8 | )
 9 | 
10 | // NameFromEmail takes an email address and returns the part that comes before the @ symbol
11 | //
12 | // Example:
13 | //
14 | //	NameFromEmail("test_user@example.com")
15 | //	> "Test_user"
16 | func NameFromEmail(email string) string {
17 | 	parts := strings.Split(email, "@")
18 | 	name := strings.ReplaceAll(parts[0], ".", " ")
19 | 
20 | 	c := cases.Title(language.English)
21 | 	return c.String(name)
22 | }
23 | 
24 | // NamesFromEmails takes a slice of email addresses and returns a slice of the parts that
25 | // come before the @ symbol
26 | //
27 | // Example:
28 | //
29 | //	NamesFromEmail("test_user@example.com", "other_user@example.com")
30 | //	> []string{"Test_user", "Other_user"}
31 | func NamesFromEmails(emails []string) []string {
32 | 	names := make([]string, len(emails))
33 | 
34 | 	for i, email := range emails {
35 | 		names[i] = NameFromEmail(email)
36 | 	}
37 | 
38 | 	return names
39 | }
40 | 


--------------------------------------------------------------------------------
/utils/email_addresses_test.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func Test_NameFromEmail(t *testing.T) {
10 | 	assert.Equal(t, "", NameFromEmail(""))
11 | 	assert.Equal(t, "Chris Cummer", NameFromEmail("chris.cummer@me.com"))
12 | }
13 | 
14 | func Test_NamesFromEmails(t *testing.T) {
15 | 	var result []string
16 | 
17 | 	result = NamesFromEmails([]string{})
18 | 	assert.Equal(t, []string{}, result)
19 | 
20 | 	result = NamesFromEmails([]string{"chris.cummer@me.com", "chriscummer@me.com"})
21 | 	assert.Equal(t, []string{"Chris Cummer", "Chriscummer"}, result)
22 | }
23 | 


--------------------------------------------------------------------------------
/utils/homedir.go:
--------------------------------------------------------------------------------
 1 | // Package homedir helps with detecting and expanding the user's home directory
 2 | 
 3 | // Copied (mostly) verbatim from https://github.com/Atrox/homedir
 4 | 
 5 | package utils
 6 | 
 7 | import (
 8 | 	"errors"
 9 | 	"os"
10 | 	"path/filepath"
11 | )
12 | 
13 | // ExpandHomeDir expands the path to include the home directory if the path
14 | // is prefixed with `~`. If it isn't prefixed with `~`, the path is
15 | // returned as-is.
16 | func ExpandHomeDir(path string) (string, error) {
17 | 	if path == "" {
18 | 		return path, nil
19 | 	}
20 | 
21 | 	if path[0] != '~' {
22 | 		return path, nil
23 | 	}
24 | 
25 | 	if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
26 | 		return "", errors.New("cannot expand user-specific home dir")
27 | 	}
28 | 
29 | 	dir, err := os.UserHomeDir()
30 | 	if err != nil {
31 | 		return "", err
32 | 	}
33 | 
34 | 	return filepath.Join(dir, path[1:]), nil
35 | }
36 | 


--------------------------------------------------------------------------------
/utils/homedir_test.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func Test_ExpandHomeDir(t *testing.T) {
10 | 	tests := []struct {
11 | 		name             string
12 | 		path             string
13 | 		expectedStart    string
14 | 		expectedContains string
15 | 		expectedError    error
16 | 	}{
17 | 		{
18 | 			name:             "with empty path",
19 | 			path:             "",
20 | 			expectedStart:    "",
21 | 			expectedContains: "",
22 | 			expectedError:    nil,
23 | 		},
24 | 		{
25 | 			name:             "with relative path",
26 | 			path:             "~/test",
27 | 			expectedStart:    "/",
28 | 			expectedContains: "/test",
29 | 			expectedError:    nil,
30 | 		},
31 | 		{
32 | 			name:             "with absolute path",
33 | 			path:             "/Users/test",
34 | 			expectedStart:    "/",
35 | 			expectedContains: "/test",
36 | 			expectedError:    nil,
37 | 		},
38 | 	}
39 | 
40 | 	for _, tt := range tests {
41 | 		t.Run(tt.name, func(t *testing.T) {
42 | 			actual, err := ExpandHomeDir(tt.path)
43 | 
44 | 			if len(tt.path) > 0 {
45 | 				assert.Equal(t, tt.expectedStart, string(actual[0]))
46 | 			}
47 | 
48 | 			assert.Contains(t, actual, tt.expectedContains)
49 | 			assert.Equal(t, tt.expectedError, err)
50 | 		})
51 | 	}
52 | }
53 | 


--------------------------------------------------------------------------------
/utils/init.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | // OpenFileUtil defines the system utility to use to open files
 4 | var OpenFileUtil = "open"
 5 | var OpenUrlUtil = []string{}
 6 | 
 7 | // Init initializes global settings in the wtf package
 8 | func Init(openFileUtil string, openUrlUtil []string) {
 9 | 	OpenFileUtil = openFileUtil
10 | 	OpenUrlUtil = openUrlUtil
11 | }
12 | 


--------------------------------------------------------------------------------
/utils/init_test.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func Test_Init(t *testing.T) {
10 | 	Init("cats", []string{"dogs"})
11 | 
12 | 	assert.Equal(t, OpenFileUtil, "cats")
13 | 	assert.Equal(t, OpenUrlUtil, []string{"dogs"})
14 | }
15 | 


--------------------------------------------------------------------------------
/utils/reflective.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"reflect"
 6 | )
 7 | 
 8 | // StringValueForProperty returns a string value for the given property
 9 | // If the property doesn't exist, it returns an error
10 | func StringValueForProperty(ref interface{}, propName string) (string, error) {
11 | 	v := reflect.ValueOf(ref)
12 | 	refVal := reflect.Indirect(v).FieldByName(propName)
13 | 
14 | 	if !refVal.IsValid() {
15 | 		return "", fmt.Errorf("invalid property name: %s", propName)
16 | 	}
17 | 
18 | 	strVal := fmt.Sprintf("%v", refVal)
19 | 
20 | 	return strVal, nil
21 | }
22 | 


--------------------------------------------------------------------------------
/utils/sums.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | // SumInts takes a slice of ints and returns the sum of them
 4 | func SumInts(vals []int) int {
 5 | 	sum := 0
 6 | 
 7 | 	for _, a := range vals {
 8 | 		sum += a
 9 | 	}
10 | 
11 | 	return sum
12 | }
13 | 


--------------------------------------------------------------------------------
/utils/sums_test.go:
--------------------------------------------------------------------------------
 1 | package utils
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/stretchr/testify/assert"
 7 | )
 8 | 
 9 | func Test_SumInts(t *testing.T) {
10 | 	expected := 6
11 | 	result := SumInts([]int{1, 3, 2})
12 | 
13 | 	assert.Equal(t, expected, result)
14 | 
15 | 	expected = 46
16 | 	result = SumInts([]int{4, 6, 7, 23, 6})
17 | 
18 | 	assert.Equal(t, expected, result)
19 | 
20 | 	expected = 4
21 | 	result = SumInts([]int{4})
22 | 
23 | 	assert.Equal(t, expected, result)
24 | }
25 | 


--------------------------------------------------------------------------------
/wtf/datetime.go:
--------------------------------------------------------------------------------
 1 | package wtf
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"time"
 6 | )
 7 | 
 8 | const (
 9 | 	// DateFormat defines the format we expect to receive dates from BambooHR in
10 | 	DateFormat = "2006-01-02"
11 | 
12 | 	// TimeFormat defines the format we expect to receive times from BambooHR in
13 | 	TimeFormat = "15:04"
14 | )
15 | 
16 | // IsToday returns TRUE if the date is today, FALSE if the date is not today
17 | func IsToday(date time.Time) bool {
18 | 	now := time.Now().Local()
19 | 
20 | 	return (date.Year() == now.Year()) &&
21 | 		(date.Month() == now.Month()) &&
22 | 		(date.Day() == now.Day())
23 | }
24 | 
25 | // PrettyDate takes a programmer-style date string and converts it
26 | // in a friendlier-to-read format
27 | func PrettyDate(dateStr string) string {
28 | 	newTime, err := time.Parse(DateFormat, dateStr)
29 | 	if err != nil {
30 | 		return dateStr
31 | 	}
32 | 
33 | 	return fmt.Sprint(newTime.Format("Jan 2, 2006"))
34 | }
35 | 
36 | // UnixTime takes a Unix epoch time (in seconds) and returns a
37 | // time.Time instance
38 | func UnixTime(unix int64) time.Time {
39 | 	return time.Unix(unix, 0)
40 | }
41 | 


--------------------------------------------------------------------------------
/wtf/enablable.go:
--------------------------------------------------------------------------------
1 | package wtf
2 | 
3 | // Enablable is the interface that enforces enable/disable capabilities on a module
4 | type Enablable interface {
5 | 	Disable()
6 | 	Disabled() bool
7 | 	Enabled() bool
8 | }
9 | 


--------------------------------------------------------------------------------
/wtf/numbers.go:
--------------------------------------------------------------------------------
 1 | package wtf
 2 | 
 3 | import "math"
 4 | 
 5 | // Round rounds a float to an integer
 6 | func Round(num float64) int {
 7 | 	return int(num + math.Copysign(0.5, num))
 8 | }
 9 | 
10 | // TruncateFloat64 truncates the decimal places of a float64 to the specified precision
11 | func TruncateFloat64(num float64, precision int) float64 {
12 | 	output := math.Pow(10, float64(precision))
13 | 	return float64(Round(num*output)) / output
14 | }
15 | 


--------------------------------------------------------------------------------
/wtf/schedulable.go:
--------------------------------------------------------------------------------
 1 | package wtf
 2 | 
 3 | import "time"
 4 | 
 5 | // Schedulable is the interface that enforces scheduling capabilities on a module
 6 | type Schedulable interface {
 7 | 	Refresh()
 8 | 	Refreshing() bool
 9 | 	RefreshInterval() time.Duration
10 | }
11 | 


--------------------------------------------------------------------------------
/wtf/stoppable.go:
--------------------------------------------------------------------------------
1 | package wtf
2 | 
3 | // Stoppable is the interface that enforces a stoppable state
4 | type Stoppable interface {
5 | 	Stop()
6 | }
7 | 


--------------------------------------------------------------------------------
/wtf/terminal.go:
--------------------------------------------------------------------------------
 1 | package wtf
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"os"
 6 | 
 7 | 	"github.com/logrusorgru/aurora/v4"
 8 | 	"github.com/olebedev/config"
 9 | )
10 | 
11 | // SetTerminal sets the TERM environment variable, defaulting to whatever the OS
12 | // has as the current value if none is specified.
13 | // See https://www.gnu.org/software/gettext/manual/html_node/The-TERM-variable.html for
14 | // more details.
15 | func SetTerminal(config *config.Config) {
16 | 	term := config.UString("wtf.term", os.Getenv("TERM"))
17 | 	err := os.Setenv("TERM", term)
18 | 	if err != nil {
19 | 		fmt.Printf("\n%s Failed to set $TERM to %s.\n", aurora.Red("ERROR"), aurora.Yellow(term))
20 | 		os.Exit(1)
21 | 	}
22 | }
23 | 


--------------------------------------------------------------------------------
/wtf/wtfable.go:
--------------------------------------------------------------------------------
 1 | package wtf
 2 | 
 3 | import (
 4 | 	"github.com/wtfutil/wtf/cfg"
 5 | 
 6 | 	"github.com/rivo/tview"
 7 | )
 8 | 
 9 | // Wtfable is the interface that enforces WTF system capabilities on a module
10 | type Wtfable interface {
11 | 	Enablable
12 | 	Schedulable
13 | 	Stoppable
14 | 
15 | 	BorderColor() string
16 | 	ConfigText() string
17 | 	FocusChar() string
18 | 	Focusable() bool
19 | 	HelpText() string
20 | 	Name() string
21 | 	QuitChan() chan bool
22 | 	SetFocusChar(string)
23 | 	TextView() *tview.TextView
24 | 
25 | 	CommonSettings() *cfg.Common
26 | }
27 | 


--------------------------------------------------------------------------------