Battle-Cattle/BCUK-Bot-3

View on GitHub
src/main/java/com/expiredminotaur/bcukbot/justgiving/JustGivingAPI.java

Summary

Maintainability
A
2 hrs
Test Coverage
package com.expiredminotaur.bcukbot.justgiving;

import com.expiredminotaur.bcukbot.HttpHandler;
import com.expiredminotaur.bcukbot.command.CommandEvent;
import com.expiredminotaur.bcukbot.discord.DiscordBot;
import com.expiredminotaur.bcukbot.discord.music.MusicHandler;
import com.expiredminotaur.bcukbot.twitch.TwitchBot;
import com.expiredminotaur.bcukbot.twitch.command.chat.TwitchCommandEvent;
import com.expiredminotaur.bcukbot.twitch.streams.LiveStreamManager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@Component
public class JustGivingAPI
{
    private final Logger log = LoggerFactory.getLogger(JustGivingAPI.class);
    private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private final TwitchBot twitchBot;
    private final DiscordBot discordBot;
    private final MusicHandler musicHandler;
    private final LiveStreamManager liveStreamManager;
    private final JustGivingSettings settings;
    private String data;
    private String totalRaisedMessage = null;
    private ScheduledFuture<?> task = null;

    @Autowired
    private JustGivingEventHandler eventHandler;

    public JustGivingAPI(@Lazy @Autowired TwitchBot twitchBot, @Lazy @Autowired DiscordBot discordBot, @Lazy @Autowired MusicHandler musicHandler, @Lazy @Autowired LiveStreamManager liveStreamManager, @Autowired JustGivingEventHandler eventHandler)
    {
        this.twitchBot = twitchBot;
        this.discordBot = discordBot;
        this.musicHandler = musicHandler;
        this.liveStreamManager = liveStreamManager;
        this.eventHandler = eventHandler;
        JustGivingSettings settings;
        try (FileReader fr = new FileReader(JustGivingSettings.getFileName()))
        {
            settings = gson.fromJson(fr, JustGivingSettings.class);
        } catch (IOException e)
        {
            log.warn("Can't load just giving settings file, this may be because one has not been made yet");
            settings = new JustGivingSettings();
        }
        this.settings = settings;
        saveSettings();
        eventHandler.update(settings.getLastTotal(), settings.getLastTarget());
    }

    public void saveSettings()
    {
        try (FileWriter fw = new FileWriter(JustGivingSettings.getFileName()))
        {
            gson.toJson(settings, fw);
        } catch (IOException e)
        {
            log.error("Unable to save just giving setting file", e);
        }
        updateScheduler();
    }

    private void updateScheduler()
    {
        if (settings.getAutoCheckEnabled() && task == null)
        {
            task = scheduler.scheduleAtFixedRate(this::checkForNewData, 0, 1, TimeUnit.SECONDS);
        } else if (!settings.getAutoCheckEnabled() && task != null)
        {
            task.cancel(false);
            task = null;
        }
    }

    private void checkForNewData()
    {
        try
        {
            URL url = new URL(String.format("https://api.justgiving.com/%s/v1/fundraising/pages/%s", settings.getAppId(), settings.getCampaignName()));
            BufferedReader br = HttpHandler.getRequest(url);
            String output;
            if (br != null && (output = br.readLine()) != null)
            {
                updateData(output);
            }
        } catch (Exception e)
        {
            log.error("Justgiving API error", e);
            try
            {
                //If the last check failed wait for 30 seconds before trying again
                Thread.sleep(30000);
            } catch (InterruptedException e2)
            {
                log.error("Justgiving Sleep Interrupted Exception", e2);
            }
        }
    }

    private void updateData(String data)
    {
        this.data = data;
        JsonElement jsonTree = JsonParser.parseString(data);
        if (jsonTree.isJsonObject())
        {
            JsonObject jsonObject = jsonTree.getAsJsonObject();
            double total = Double.parseDouble(jsonObject.get("grandTotalRaisedExcludingGiftAid").getAsString());
            double target = Double.parseDouble(jsonObject.get("fundraisingTarget").getAsString());
            if (total > settings.getLastTotal())
            {
                settings.setLastTotal(total);
                settings.setLastTarget(target);
                saveSettings();
                updateTotalRaisedMessage();
                sendMessageToAll();
                sendMessageToDiscord();
                sendMessageToFacebook();
                musicHandler.loadAndPlayPriority("justgiving.mp3");
                eventHandler.update(total, target);
            } else if (totalRaisedMessage == null || target != settings.getLastTarget())
            {
                settings.setLastTarget(target);
                saveSettings();
                updateTotalRaisedMessage();
                eventHandler.update(total, target);
            }
        }
    }

    private void sendMessageToAll()
    {
        if (totalRaisedMessage != null && settings.getChannels() != null)
        {
            for (String channel : settings.getChannels())
                twitchBot.sendMessage(channel, totalRaisedMessage);
        }
    }

    private void sendMessageToDiscord()
    {
        if (totalRaisedMessage != null && settings.getDiscordChannelIds() != null)
        {
            for (long channel : settings.getDiscordChannelIds())
                discordBot.sendMessage(channel, totalRaisedMessage);
        }
    }

    private void sendMessageToFacebook()
    {
        try
        {
            if (totalRaisedMessage != null && settings.getFacebookWebhook() != null)
            {
                JsonObject json = new JsonObject();
                json.addProperty("message", totalRaisedMessage);
                json.addProperty("link", settings.getCampaignLink());
                HttpHandler.postUTF8Request(new URL(settings.getFacebookWebhook()), json.toString());
            }
        } catch (Exception e)
        {
            log.error("Error with facebook post", e);
        }
    }

    public Mono<Void> amountRaised(CommandEvent<?> event)
    {
        if (totalRaisedMessage != null)
        {
            if (event instanceof TwitchCommandEvent)
                return ((TwitchCommandEvent) event).multiRespond(liveStreamManager, totalRaisedMessage);
            else
                return event.respond(totalRaisedMessage);
        }
        return event.empty();
    }

    private void updateTotalRaisedMessage()
    {
        JsonElement jsonTree = JsonParser.parseString(data);
        JsonObject jsonObject = jsonTree.getAsJsonObject();
        String total = toCurrency(jsonObject.get("grandTotalRaisedExcludingGiftAid").getAsString());
        String target = toCurrency(jsonObject.get("fundraisingTarget").getAsString());
        String percentage = jsonObject.get("totalRaisedPercentageOfFundraisingTarget").getAsString() + "%";
        String message = settings.getMessage();
        message = message.replace("$total", total);
        message = message.replace("$target", target);
        message = message.replace("$percentage", percentage);
        totalRaisedMessage = message;
    }

    private String toCurrency(String amount)
    {
        String[] split = amount.split("\\.");
        if (split.length < 2)
            return "£" + split[0] + ".00";
        else if (split[1].length() < 2)
            return "£" + split[0] + "." + split[1] + "0";
        else
            return "£" + amount;
    }

    public JustGivingSettings getSettings()
    {
        return settings;
    }
}