Code Monkey home page Code Monkey logo

new-kyle's Introduction

New-Kyle

Stars CodeFactor Build Status Discord

Welcome! This project is an open-source Kotlin-based Discord bot that uses the JDA and Josh Grosh's JDA Utilities.

Introduction

This project is a collaboration between myself and a few of my friends. It is a proof of concept and learning tool for those who wish to learn modern development practices and tools. It was inspired by one of our friends who used to be in our Discord server. The functionality contained within is supposed to emulate his mannerism as closely as possible.

Build and Deployment Instructions

This application can be run either from the latest published release, built from source, or deployed using docker. The application depends on four environmental variables being set.

Environment variable Description Example
DISCORD_TOKEN Bot token DRSdfL2sRBB4d54Rs5DSTp69C4TRAFb1.9aqaVAPiod6Ma5xC6HMwWQ8rvvMA43ATD9Uwkpcq5LVQUw
DISCORD_OWNER Owner user id 965407413600764227
DISCORD_GAME Game My Game
DISCORD_PREFIX Bot command prefix !kyle

Build locally

To build from source and run locally, set the environment variables and run the bootRun task using the gradle wrapper script:

Windows
  set DISCORD_TOKEN=your-bot-token-here
  set DISCORD_OWNER=your-discord-id-here
  set DISCORD_GAME=My Game
  set DISCORD_PREFIX=!kyle 
  .\gradlew.bat bootRun
Linux
  export DISCORD_TOKEN=your-bot-token-here
  export DISCORD_OWNER=your-discord-id-here
  export DISCORD_GAME=My Game
  export DISCORD_PREFIX='!kyle '
  ./gradlew bootRun

Current State and Future Plans

As it stands, this bot is simply a testbed and learning platform for myself and my friends to get acquainted with modern development tools and APIs. Eventually, I plan on taking the things that were most valuable in this project and moving on to develop a more serious, useful bot for a wider audience.

For now, it is purely for recreation. More updates will be posted as more functionality of the bot becomes available.

new-kyle's People

Contributors

hachbox avatar kjm015 avatar lordshinu avatar tdickens1234 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

uniturtle1

new-kyle's Issues

[feature] Implement a message template service for outgoing messages

Per issue #42, creating an issue to track the TODO that appears on line 14 below:

/**
* This class handles events where users join a server or are unbanned from it.
* Eventually, this class should implement a message template service for its
* messages, as this allows for a greater variety of messages.
*
* TODO: Implement templates for outgoing messages
*
* @author kjm015
* @since 01/11/2019
*/
class InfluxListener : ListenerAdapter() {

[docs] Add build/install/deploy instructions to README.md

Is your feature request related to a problem? Please describe.
For people who are not already familiar with the tech stack, it may not be immediately obvious how to go about building the software or what environment variables are required to run the application.

Describe the solution you'd like
Add a short section to the README with documentation about the environment variables used, and how to build and run the application using the gradle wrapper scripts.

Describe alternatives you've considered

  • Publish application as a .jar to the releases page, and add instructions on how to run the jar to the README.
  • Publish a docker image to Docker Hub or GitHub Container registry, and add instructions on how to run the docker container to the README.

[bug] Can't start in jar

Describe the bug
Gradle is able to complete the assemble, build, bootJar, bootRun, and test, tasks. When executing via bootRun, the application runs normally. However, when executing the jar, the application crashes with a stack trace showing the following error: java.io.FileNotFoundException: class path resource [computer/gpu.json] cannot be resolved to absolute file path because it does not reside in the file system.

To Reproduce
Steps to reproduce the behavior:

  1. Build the jar.
  gradle bootJar
  1. Set appropriate environment variables.
  2. Execute the jar
  java -jar build/libs/kyle-newer-0.0.2-SNAPSHOT.jar
  1. Wait for Spring to initialize.
  2. Observe crash and stack trace.

Expected behavior
The jar file does actually contain the "missing" file at the correct location, so it should work without crashing.

Screenshots
image
Stacktrace text:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-04-25 04:29:31.113 ERROR 319 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: Failed to execute ApplicationRunner
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:771) ~[spring-boot-2.6.6.jar!/:2.6.6]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:758) ~[spring-boot-2.6.6.jar!/:2.6.6]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.6.jar!/:2.6.6]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.6.jar!/:2.6.6]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.6.jar!/:2.6.6]
        at io.github.kjm015.kylenewer.KyleNewerApplicationKt.main(KyleNewerApplication.kt:85) ~[classes!/:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[kyle-newer-0.0.2-SNAPSHOT.jar:na]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[kyle-newer-0.0.2-SNAPSHOT.jar:na]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[kyle-newer-0.0.2-SNAPSHOT.jar:na]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[kyle-newer-0.0.2-SNAPSHOT.jar:na]
Caused by: java.io.FileNotFoundException: class path resource [computer/cases.json] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/mnt/c/Projects/New-Kyle/build/libs/kyle-newer-0.0.2-SNAPSHOT.jar!/BOOT-INF/classes!/computer/cases.json
        at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:217) ~[spring-core-5.3.18.jar!/:5.3.18]
        at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:180) ~[spring-core-5.3.18.jar!/:5.3.18]
        at io.github.kjm015.kylenewer.util.computer.compcase.CaseLoader.retrieveFromFile(CaseLoader.kt:23) ~[classes!/:na]
        at io.github.kjm015.kylenewer.util.computer.compcase.CaseLoader.run(CaseLoader.kt:15) ~[classes!/:na]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768) ~[spring-boot-2.6.6.jar!/:2.6.6]
        ... 13 common frames omitted

Desktop (please complete the following information):

  • OS: Linux 5.10.102 / Debian 10 (buster)
  • Java: OpenJDK 64-Bit 17.0.2+8-86 2022-01-18
  • Package version: 0.0.2-SNAPSHOT
  • Commit: f7fa67d

Additional context
The code responsible for the crash is in PowerSupplyLoader.kt#L22-L23.

val file = ResourceUtils.getFile("classpath:computer/psu.json")
val list = mapper.readValue(file, PSUList::class.java)

ResourceUtils::getFile(String resourceLocation) always returns a java.io.File object. That works fine when the resourceLocation is pointing to a location on the file system, but it does not work when the resourceLocation is pointing to a path within a jar file, since items inside a jar archive do not exist as individual files on the file system and therefore cannot be java.io.File objects.

Potential solution
In the above code ObjectMapper::readValue(File src, Class<T> valueType) deserializes the json file. However, readValue can read from multiple input types, not just File:

  • byte[]
  • java.io.File
  • java.io.InputStream
  • java.io.Reader
  • java.lang.String
  • java.net.URL

To support jar files, the ResourceUtils::getURL(String resourceLocation) method can be
called in an identical fashion to return a java.net.URL object which can be consumed by
ObjectMapper::readValue(URL src, Class<T> valueType) in the exactly same way as a
ObjectMapper::readValue(File src, Class<T> valueType) without changing any other logic.

[bug] computer command budget logic is buggy

Describe the bug

  • The bot does not make full use of the budget, even when it is capable of generating a more expensive build within the budget constraints. For example, if given a budget of 850, it will spit out a build costing 826.91, but given a budget of 827, it does not provide that build.
  • Some budgets cause the bot to throw a NullPointerException, which is always interpreted as a budget that is too low, even when the budget is higher than many valid builds.

To Reproduce
Steps to reproduce the behavior:

  1. Ask for a computer with budget of various values between 800-875.

    !kyle computer 850

  2. Notice that the whole budget is not used.

    Total Cost | $826.91

  3. Ask for a computer with budget of 900 or 1000.

    !kyle computer 900

  4. Notice that the bot acts as if the budget is too low.

    Sorry, it seems you can't afford that right about now!

Expected behavior
The bot should always return the best computer available at a given budget, including budget of 900 and 1000.

Screenshots
kyle-computer-bug

Additional context
Terminal output:

2022-04-26 02:39:09.695 ERROR 536 --- [inWS-ReadThread] i.g.k.newkyle.commands.ComputerCommand   : Failed to create PC for user "chigeek" -> java.lang.NullPointerException

[clean] Update stale links in README.md

Is your feature request related to a problem? Please describe.
The README.md has some stale badge URLs from before the repository was renamed from Kyle-Newer to New-Kyle. The github stargazers badge still works because stars are automatically redirected when a github repository is renamed. However, the codefactor badge is a now broken image, and the travis-ci badge shows build|unknown.

Describe the solution you'd like

  • Update the stale URLs to reflect the new repository name "New-Kyle".
  • Reconnect Travis CI.

Describe alternatives you've considered
Revert back to the old repository name instead of fixing the links.

Additional context
image

[feature] Add docker support

Is your feature request related to a problem? Please describe.
This project is difficult to build and deploy reliably, especially for those not familiar with this tech stack. It requires specific versions of JDK and other dependencies, and environmental variables which are not documented in the README.md.

Describe the solution you'd like
Adding Dockerfile, docker-compose.yml, and example.env files to the root of the repository along with a short information section in the README.md would greatly ease the process of building and deploying, especially for newcomers.

Describe alternatives you've considered
Vendor-specific implementations for automated builds and one-click deployments:

[bug] gradle assemble fails in Travis CI due to certificate issue for jcenter URL

Describe the bug
When travis runs the ./gradlew assemble command, the :compileKotlin task fails with error "peer not authenticated"

To Reproduce
Steps to reproduce the behavior:

  1. Push a commit
  2. Visit https://app.travis-ci.com/github/kjm015/New-Kyle
  3. Scroll down to about line 197 and expand the install step with command ./gralde assemble
  4. Scroll down to about lines 213-227

Expected behavior
The "peer not authenticated" error means that there was a problem with the TLS handshake. The certificate for jcenter.bintray.com is a valid for *.bintray.com, signed by DigiCeter, from Mon, 25 Oct 2021 00:00:00 GM. The URL https://jcenter.bintray.com/com/jagrosh/JLyrics/0.4/JLyrics-0.4.pom can be downloaded via curl 7.47.0 on Ubuntu 16.04 without issues, so it should be able to be downloaded by Travis CI as well.

Screenshots
image
image
Text:

$ javac -J-Xmx32m -version
javac 11.0.2
$ ./gradlew assemble
[...]
> Task :compileKotlin FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':compileKotlin'.
> Error while evaluating property 'filteredArgumentsMap' of task ':compileKotlin'
   > Could not resolve all files for configuration ':compileClasspath'.
      > Could not resolve com.jagrosh:JLyrics:0.4.
        Required by:
            project :
         > Could not resolve com.jagrosh:JLyrics:0.4.
            > Could not get resource 'https://jcenter.bintray.com/com/jagrosh/JLyrics/0.4/JLyrics-0.4.pom'.
               > Could not GET 'https://jcenter.bintray.com/com/jagrosh/JLyrics/0.4/JLyrics-0.4.pom'.
                  > peer not authenticated

Build system information

Build language: java
Build dist: xenial
Runtime kernel version: 4.15.0-1098-gcp
openjdk version "11.0.2" 2019-01-15
Gradle 7.4.2

Additional context
TLS errors can be caused by older versions of JDK which can have different implementations of TLS, with different TLS versions supported (TLS 1.2, TLS 1.3), different behavior for TLS client hello, and different trusted root certificate authorities. Some possible workarounds for similar problems found online included:

  • Changing the repository URL from https to http to avoid TLS altogether.
  • Upgrading to a newer version of JDK, eg from OpenJDK 7 to Oracle JDK 8, or upgrading from OpenJDK 11 to OpenJDK 13.
  • Manually adding certificates to the trusted key store.

Potential avenues to explore

  • The version of OpenJDK that Travis CI is using (11.0.2) is pretty old, and nearing end of support (Oracle Java Premier Support: September 2023, RedHat OpenJDK Support: October 2024, Oracle Java Extended Support: September 2026). The file build.gradle.kts defines the Java compatibility at JavaVersion.VERSION_12, so JDK 11 would not be appropriate. Building with OpenJDK 17.0.2 does work locally, and might solve the problem in Travis. Per the Travis CI docs on Using Java 10 and later, the .travis.yml can be modified to use a specific JDK version.
  • Ubuntu 16.04 "xenial" is also pretty old and is no longer within standard support. Might as well change that at the same time. Newer releases are supported:
    Version Code name End of Standard Support
    Ubuntu 22.04 Jammy Jellyfish April 2027
    Ubuntu 21.10 Impish Indri July 14, 2022
    Ubuntu 20.04 LTS Focal Fossa April 2025
    Ubuntu 18.04 LTS Bionic Beaver April 2023
    Ubuntu 16.04 LTS Xenial Xerus April 2021

[clean] Refactor code to address CodeFactor issues in cleanup branch for PR #37

Is your feature request related to a problem? Please describe.
CodeFactor is currently complaining about two minor issues in the cleanup branch that should be fixed prior to PR #37.

CodeFactor warnings included below for convenience:


Severity: Minor Complex Method complexity = 17
Long or complex methods may lead to code that is difficult to understand and change. Such code may also attract abusive use of comments when a small method or variable are viable alternatives. The key to understand about small methods is good naming. If you have a good name for a method you don't need to look at the body.

Refactorings

fun buildComputer(budget: Double = 900.00, requester: String = "Someone"): Computer {
val cpuBudget = budget * 0.20
val gpuBudget = budget * 0.37
val mobBudget = budget * 0.13
val ramBudget = budget * 0.08
val casBudget = budget * 0.07
val psuBudget = budget * 0.08
val stoBudget = budget * 0.07
var cpu = cpuRepository.findAll().filter {
it.price <= cpuBudget
}.maxByOrNull { it.price } ?: cpuRepository.findAll().minByOrNull { it.price }!!
val mob = motherboardRepository.findAll().filter {
it.socket == cpu.socket && it.price <= mobBudget
}.maxByOrNull { it.price } ?: motherboardRepository.findAll().filter {
it.socket == cpu.socket && it.price <= mobBudget
}.minByOrNull { it.price }!!
val ram = memoryRepository.findAll().filter {
it.price <= ramBudget && it.moduleCount * it.moduleCapacityGB < mob.maxMemoryLimitGB && it.speed <= mob.maxMemorySpeed
}.maxByOrNull { it.price }!!
val gpu = gpuRepository.findAll().filter {
it.price <= gpuBudget
}.maxByOrNull { it.price } ?: gpuRepository.findAll().minByOrNull { it.price }!!
val case = caseRepository.findAll().filter {
it.price <= casBudget && it.formFactor == mob.formFactor && it.maxGPULength >= gpu.length
}.maxByOrNull { it.price }!!
val psu = powerSupplyRepository.findAll().filter {
it.price <= psuBudget && it.formFactor == case.psuFormFactor
}.maxByOrNull { it.price }!!
val storage = arrayListOf(storageRepository.findAll().filter { it.price <= stoBudget }.maxByOrNull { it.price }!!)
var cost = cpu.price + mob.price + gpu.price + ram.price + case.price + psu.price + storage.first().price
var remainingBudget = budget - cost
val cooler = coolerRepository.findAll().filter {
it.price <= remainingBudget / 1.5 && it.supportedSockets.contains(mob.socket) && (it.radiatorSize <= case.maxRadiatorSupport)
}.maxByOrNull {
it.price
} ?: if (cpu.includedCoolerName != null)
coolerRepository.findByName(cpu.includedCoolerName!!)
else
CPUCooler(
id = -1L,
name = "",
manufacturer = "",
isLiquidCooler = false,
radiatorSize = 0,
fanSize = 0,
fanCount = 0,
hasRGB = false,
height = 0,
supportedSockets = arrayListOf(),
price = 0.00
)
remainingBudget -= cooler.price
cost += cooler.price
cpu = cpuRepository.findAllByPriceLessThan(cpuBudget + remainingBudget).filter { it.socket == mob.socket }
.maxByOrNull { it.price }!!
while (remainingBudget > storageRepository.findAll().minByOrNull { it.price }!!.price && storage.size < 3) {
val tempStorage = storageRepository.findAll().filter { it.price <= remainingBudget }.maxByOrNull { it.price }!!
storage.add(tempStorage)
remainingBudget -= tempStorage.price
cost += tempStorage.price
}
return Computer(
cpu = cpu,
gpu = gpu,
motherboard = mob,
memory = ram,
case = case,
powerSupply = psu,
cooler = cooler,
name = "${requester}'s PC",
owner = requester,
storage = storage,
price = cost
)
}


Severity: Major This comment contains 'TODO:' that has been defined as forbidden in detekt. lines of code = 1
Developers often add comments to code which is not complete or needs review. Most likely you want to fix or review the code, and then remove the comment, before you consider the code to be production ready.

Further Reading

Have your own Detekt rules? No problem, simply add detekt.yml file to the root of this branch.
issue code: detekt.ForbiddenComment

* TODO: Implement templates for outgoing messages


Describe the solution you'd like

  • Follow the Source Making recommendations for refactoring the complex method.
  • Create an issue to track the planned enhancement and remove the TODO comment.

Describe alternatives you've considered

  • Ignore the complex method for now, since it is a low severity warning.
  • Create a detekt.yml file to allow TODOs in javadoc.

Additional context
Some IDEs can track TODOs that appear inside javadoc as tasks. However, these tasks might be better tracked in a project management tool, such as GitHub issues, where they can be prioritized appropriately.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.