First Commit, working play feature.
This commit is contained in:
parent
cad267befa
commit
c0477a4d17
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>net.agspace.handiebot</groupId>
|
||||
<artifactId>handiebot</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jcenter</id>
|
||||
<url>http://jcenter.bintray.com</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.austinv11</groupId>
|
||||
<artifactId>Discord4J</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sedmelluq</groupId>
|
||||
<artifactId>lavaplayer</artifactId>
|
||||
<version>1.2.39</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,149 @@
|
|||
package handiebot;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import handiebot.command.CommandHandler;
|
||||
import handiebot.lavaplayer.GuildMusicManager;
|
||||
import sx.blah.discord.api.ClientBuilder;
|
||||
import sx.blah.discord.api.IDiscordClient;
|
||||
import sx.blah.discord.api.events.EventSubscriber;
|
||||
import sx.blah.discord.handle.audio.IAudioManager;
|
||||
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
|
||||
import sx.blah.discord.handle.obj.IChannel;
|
||||
import sx.blah.discord.handle.obj.IGuild;
|
||||
import sx.blah.discord.handle.obj.IVoiceChannel;
|
||||
import sx.blah.discord.util.DiscordException;
|
||||
import sx.blah.discord.util.MissingPermissionsException;
|
||||
import sx.blah.discord.util.RateLimitException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
* Main Class for the discord bot. Contains client loading information and general event processing.
|
||||
*/
|
||||
public class HandieBot {
|
||||
|
||||
private static final String TOKEN = "MjgzNjUyOTg5MjEyNjg4Mzg0.C45A_Q.506b0G6my1FEFa7_YY39lxLBHUY";
|
||||
|
||||
private static IDiscordClient client;
|
||||
|
||||
private CommandHandler commandHandler;
|
||||
|
||||
public static void main(String[] args) throws DiscordException, RateLimitException {
|
||||
System.out.println("Logging bot in...");
|
||||
client = new ClientBuilder().withToken(TOKEN).build();
|
||||
client.getDispatcher().registerListener(new HandieBot());
|
||||
client.login();
|
||||
}
|
||||
|
||||
private final AudioPlayerManager playerManager;
|
||||
private final Map<Long, GuildMusicManager> musicManagers;
|
||||
|
||||
private HandieBot() {
|
||||
this.musicManagers = new HashMap<>();
|
||||
this.playerManager = new DefaultAudioPlayerManager();
|
||||
AudioSourceManagers.registerRemoteSources(playerManager);
|
||||
AudioSourceManagers.registerLocalSource(playerManager);
|
||||
|
||||
this.commandHandler = new CommandHandler(this);
|
||||
}
|
||||
|
||||
private synchronized GuildMusicManager getGuildAudioPlayer(IGuild guild) {
|
||||
long guildId = Long.parseLong(guild.getID());
|
||||
GuildMusicManager musicManager = musicManagers.get(guildId);
|
||||
|
||||
if (musicManager == null) {
|
||||
musicManager = new GuildMusicManager(playerManager);
|
||||
musicManagers.put(guildId, musicManager);
|
||||
}
|
||||
|
||||
guild.getAudioManager().setAudioProvider(musicManager.getAudioProvider());
|
||||
|
||||
return musicManager;
|
||||
}
|
||||
|
||||
@EventSubscriber
|
||||
public void onMessageReceived(MessageReceivedEvent event) {
|
||||
this.commandHandler.handleCommand(event);
|
||||
}
|
||||
|
||||
public void loadAndPlay(final IChannel channel, final String trackUrl) {
|
||||
GuildMusicManager musicManager = getGuildAudioPlayer(channel.getGuild());
|
||||
|
||||
playerManager.loadItemOrdered(musicManager, trackUrl, new AudioLoadResultHandler() {
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack track) {
|
||||
sendMessageToChannel(channel, "Adding to queue " + track.getInfo().title);
|
||||
|
||||
play(channel.getGuild(), musicManager, track);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist) {
|
||||
AudioTrack firstTrack = playlist.getSelectedTrack();
|
||||
|
||||
if (firstTrack == null) {
|
||||
firstTrack = playlist.getTracks().get(0);
|
||||
}
|
||||
|
||||
sendMessageToChannel(channel, "Adding to queue " + firstTrack.getInfo().title + " (first track of playlist " + playlist.getName() + ")");
|
||||
|
||||
play(channel.getGuild(), musicManager, firstTrack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
sendMessageToChannel(channel, "Nothing found by " + trackUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException exception) {
|
||||
sendMessageToChannel(channel, "Could not play: " + exception.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void play(IGuild guild, GuildMusicManager musicManager, AudioTrack track) {
|
||||
connectToFirstVoiceChannel(guild.getAudioManager());
|
||||
|
||||
musicManager.scheduler.queue(track);
|
||||
}
|
||||
|
||||
public void skipTrack(IChannel channel) {
|
||||
GuildMusicManager musicManager = getGuildAudioPlayer(channel.getGuild());
|
||||
musicManager.scheduler.nextTrack();
|
||||
|
||||
sendMessageToChannel(channel, "Skipped to next track.");
|
||||
}
|
||||
|
||||
private void sendMessageToChannel(IChannel channel, String message) {
|
||||
try {
|
||||
channel.sendMessage(message);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void connectToFirstVoiceChannel(IAudioManager audioManager) {
|
||||
for (IVoiceChannel voiceChannel : audioManager.getGuild().getVoiceChannels()) {
|
||||
if (voiceChannel.isConnected()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (IVoiceChannel voiceChannel : audioManager.getGuild().getVoiceChannels()) {
|
||||
try {
|
||||
voiceChannel.join();
|
||||
} catch (MissingPermissionsException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package handiebot.command;
|
||||
|
||||
import com.sun.istack.internal.NotNull;
|
||||
import handiebot.HandieBot;
|
||||
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
|
||||
import sx.blah.discord.handle.obj.*;
|
||||
import sx.blah.discord.util.EmbedBuilder;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
* Class to process commands.
|
||||
*/
|
||||
public class CommandHandler {
|
||||
|
||||
private static String PREFIX = "!";
|
||||
|
||||
private final HandieBot bot;
|
||||
|
||||
public CommandHandler(HandieBot bot){
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method to handle user messages.
|
||||
* @param event The event generated by the message.
|
||||
*/
|
||||
public void handleCommand(MessageReceivedEvent event){
|
||||
IMessage message = event.getMessage();
|
||||
IUser user = event.getAuthor();
|
||||
IChannel channel = event.getChannel();
|
||||
IGuild guild = event.getGuild();
|
||||
String command = extractCommand(message);
|
||||
String[] args = extractArgs(message);
|
||||
if (guild != null && command != null){
|
||||
if (command.equals("play") && args.length == 1){
|
||||
this.bot.loadAndPlay(channel, args[0]);
|
||||
} else if (command.equals("help")){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a command word, if one exists, from a given message.
|
||||
* @param message The message to get a command from.
|
||||
* @return The command word, minus the prefix, or null.
|
||||
*/
|
||||
private String extractCommand(IMessage message){
|
||||
String[] words = message.getContent().split(" ");
|
||||
if (words[0].startsWith(PREFIX)){
|
||||
return words[0].replaceFirst(PREFIX, "").toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a list of arguments from a message, assuming a command exists.
|
||||
* @param message The message to parse.
|
||||
* @return A list of strings representing args.
|
||||
*/
|
||||
@NotNull
|
||||
private String[] extractArgs(IMessage message){
|
||||
String[] words = message.getContent().split(" ");
|
||||
if (words[0].startsWith(PREFIX)){
|
||||
String[] args = new String[words.length-1];
|
||||
for (int i = 0; i < words.length-1; i++){
|
||||
args[i] = words[i+1];
|
||||
}
|
||||
return args;
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
private void sendHelpInfo(IUser user){
|
||||
IPrivateChannel pm = user.getOrCreatePMChannel();
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix used to identify commands.
|
||||
* @param prefix The prefix appended to the beginning of commands.
|
||||
*/
|
||||
public void setPrefix(String prefix){
|
||||
PREFIX = prefix;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package handiebot.lavaplayer;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
*/
|
||||
public class AudioLoadResultHandler implements com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler {
|
||||
|
||||
private TrackScheduler scheduler;
|
||||
|
||||
public AudioLoadResultHandler(TrackScheduler scheduler){
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack audioTrack) {
|
||||
System.out.println("Adding to queue "+ audioTrack.getInfo().title);
|
||||
scheduler.queue(audioTrack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist audioPlaylist) {
|
||||
System.out.println("Adding playlist to queue.");
|
||||
audioPlaylist.getTracks().forEach(track -> this.scheduler.queue(track));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
System.out.println("No matches!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException e) {
|
||||
System.out.println("Load failed.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package handiebot.lavaplayer;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
|
||||
import sx.blah.discord.handle.audio.AudioEncodingType;
|
||||
import sx.blah.discord.handle.audio.IAudioProvider;
|
||||
|
||||
/**
|
||||
* Created by Andrew's Computer on 18-Jun-17.
|
||||
*/
|
||||
public class AudioProvider implements IAudioProvider {
|
||||
private final AudioPlayer audioPlayer;
|
||||
private AudioFrame lastFrame;
|
||||
|
||||
/**
|
||||
* @param audioPlayer Audio player to wrap.
|
||||
*/
|
||||
public AudioProvider(AudioPlayer audioPlayer) {
|
||||
this.audioPlayer = audioPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
if (lastFrame == null) {
|
||||
lastFrame = audioPlayer.provide();
|
||||
}
|
||||
|
||||
return lastFrame != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] provide() {
|
||||
if (lastFrame == null) {
|
||||
lastFrame = audioPlayer.provide();
|
||||
}
|
||||
|
||||
byte[] data = lastFrame != null ? lastFrame.data : null;
|
||||
lastFrame = null;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannels() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioEncodingType getAudioEncodingType() {
|
||||
return AudioEncodingType.OPUS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package handiebot.lavaplayer;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
* Holds the player and track scheduler for a guild.
|
||||
*/
|
||||
public class GuildMusicManager {
|
||||
|
||||
public final AudioPlayer player;
|
||||
|
||||
public final TrackScheduler scheduler;
|
||||
|
||||
public GuildMusicManager(AudioPlayerManager manager){
|
||||
this.player = manager.createPlayer();
|
||||
this.scheduler = new TrackScheduler(this.player);
|
||||
this.player.addListener(this.scheduler);
|
||||
}
|
||||
|
||||
public AudioProvider getAudioProvider(){
|
||||
return new AudioProvider(this.player);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package handiebot.lavaplayer;
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
*/
|
||||
public class TrackScheduler extends AudioEventAdapter {
|
||||
|
||||
private final AudioPlayer player;
|
||||
private final BlockingQueue<AudioTrack> queue;
|
||||
|
||||
/**
|
||||
* Constructs a new track scheduler with the given player.
|
||||
* @param player The audio player this scheduler uses.
|
||||
*/
|
||||
public TrackScheduler(AudioPlayer player){
|
||||
this.player = player;
|
||||
this.queue = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the next track to the queue or play right away if nothing is in the queue.
|
||||
* @param track The track to play or add to the queue.
|
||||
*/
|
||||
public void queue(AudioTrack track){
|
||||
if (!player.startTrack(track, true)){
|
||||
System.out.println("Unable to start track immediately, adding to queue.");
|
||||
queue.offer(track);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the next track, stopping the current one if it's playing.
|
||||
*/
|
||||
public void nextTrack(){
|
||||
player.startTrack(queue.poll(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
||||
if (endReason.mayStartNext){
|
||||
nextTrack();
|
||||
} else {
|
||||
System.out.println(endReason.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception){
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
|
||||
super.onTrackStuck(player, track, thresholdMs);
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
|
@ -0,0 +1,121 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 256 256"
|
||||
id="svg4136"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:export-filename="C:\Users\AndrewComputer\Documents\Programming\IntelliJ_Projects\handiebot\src\main\resources\icon.png"
|
||||
inkscape:export-xdpi="360"
|
||||
inkscape:export-ydpi="360">
|
||||
<defs
|
||||
id="defs4138">
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow1Lstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4701"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#ffffff;stroke-width:1pt;stroke-opacity:1;fill:#ffffff;fill-opacity:1"
|
||||
transform="scale(0.8) translate(12.5,0)" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#00024b"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="104.72807"
|
||||
inkscape:cy="108.47214"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata4141">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-796.36219)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 26.071429,935.93362 C 83.571429,805.57648 182.26443,803.4602 238.57143,935.57647 226.96157,777.68558 38.855639,775.90407 26.071429,935.93362 Z"
|
||||
id="path4689"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<ellipse
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:8.58631611;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path4979"
|
||||
cx="223.82457"
|
||||
cy="925.21936"
|
||||
rx="19.245705"
|
||||
ry="31.492556" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#path4979"
|
||||
id="use4986"
|
||||
transform="translate(-181.78571,-4.4666172e-7)"
|
||||
width="100%"
|
||||
height="100%" />
|
||||
<g
|
||||
id="g4981">
|
||||
<ellipse
|
||||
ry="73.582115"
|
||||
rx="83.534172"
|
||||
cy="936.95587"
|
||||
cx="132.5"
|
||||
id="path4691"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ff6900;stroke-width:5.30872345;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4695"
|
||||
d="m 78.571429,919.50505 c 0.357143,-21.78572 30.000001,-21.42857 30.357151,0"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:7.99999905;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<use
|
||||
height="100%"
|
||||
width="100%"
|
||||
transform="matrix(-1,0,0,1,260.35717,0.35714286)"
|
||||
id="use4977"
|
||||
xlink:href="#path4695"
|
||||
y="0"
|
||||
x="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.3 KiB |
Loading…
Reference in New Issue