mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-11-27 20:57:35 +01:00
#1254 Create command to see recently logged in players
- Create datasource method to fetch most recent players by last login date - Add command to view last logged in players
This commit is contained in:
parent
7932c1bf90
commit
50dbbb8d87
@ -17,6 +17,7 @@ import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand;
|
|||||||
import fr.xephi.authme.command.executable.authme.PurgeCommand;
|
import fr.xephi.authme.command.executable.authme.PurgeCommand;
|
||||||
import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand;
|
import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand;
|
||||||
import fr.xephi.authme.command.executable.authme.PurgePlayerCommand;
|
import fr.xephi.authme.command.executable.authme.PurgePlayerCommand;
|
||||||
|
import fr.xephi.authme.command.executable.authme.RecentPlayersCommand;
|
||||||
import fr.xephi.authme.command.executable.authme.RegisterAdminCommand;
|
import fr.xephi.authme.command.executable.authme.RegisterAdminCommand;
|
||||||
import fr.xephi.authme.command.executable.authme.ReloadCommand;
|
import fr.xephi.authme.command.executable.authme.ReloadCommand;
|
||||||
import fr.xephi.authme.command.executable.authme.SetEmailCommand;
|
import fr.xephi.authme.command.executable.authme.SetEmailCommand;
|
||||||
@ -433,6 +434,15 @@ public class CommandInitializer {
|
|||||||
.executableCommand(MessagesCommand.class)
|
.executableCommand(MessagesCommand.class)
|
||||||
.register();
|
.register();
|
||||||
|
|
||||||
|
CommandDescription.builder()
|
||||||
|
.parent(authmeBase)
|
||||||
|
.labels("recent")
|
||||||
|
.description("See players who have recently logged in")
|
||||||
|
.detailedDescription("Shows the last players that have logged in.")
|
||||||
|
.permission(AdminPermission.SEE_RECENT_PLAYERS)
|
||||||
|
.executableCommand(RecentPlayersCommand.class)
|
||||||
|
.register();
|
||||||
|
|
||||||
CommandDescription.builder()
|
CommandDescription.builder()
|
||||||
.parent(authmeBase)
|
.parent(authmeBase)
|
||||||
.labels("debug", "dbg")
|
.labels("debug", "dbg")
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package fr.xephi.authme.command.executable.authme;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import fr.xephi.authme.command.ExecutableCommand;
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.DataSource;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.time.Instant.ofEpochMilli;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command showing the most recent logged in players.
|
||||||
|
*/
|
||||||
|
public class RecentPlayersCommand implements ExecutableCommand {
|
||||||
|
|
||||||
|
/** DateTime formatter, producing Strings such as "10:42 AM, 11 Jul". */
|
||||||
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("hh:mm a, dd MMM");
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeCommand(CommandSender sender, List<String> arguments) {
|
||||||
|
List<PlayerAuth> recentPlayers = dataSource.getRecentlyLoggedInPlayers();
|
||||||
|
|
||||||
|
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Recently logged in players");
|
||||||
|
for (PlayerAuth auth : recentPlayers) {
|
||||||
|
sender.sendMessage(formatPlayerMessage(auth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
ZoneId getZoneId() {
|
||||||
|
return ZoneId.systemDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatPlayerMessage(PlayerAuth auth) {
|
||||||
|
LocalDateTime lastLogin = LocalDateTime.ofInstant(ofEpochMilli(auth.getLastLogin()), getZoneId());
|
||||||
|
String lastLoginText = DATE_FORMAT.format(lastLogin);
|
||||||
|
|
||||||
|
return "- " + auth.getRealName() + " (" + lastLoginText + " with IP " + auth.getLastIp() + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -263,6 +263,11 @@ public class CacheDataSource implements DataSource {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
|
||||||
|
return source.getRecentlyLoggedInPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidateCache(String playerName) {
|
public void invalidateCache(String playerName) {
|
||||||
cachedAuths.invalidate(playerName);
|
cachedAuths.invalidate(playerName);
|
||||||
|
@ -225,6 +225,13 @@ public interface DataSource extends Reloadable {
|
|||||||
*/
|
*/
|
||||||
List<PlayerAuth> getAllAuths();
|
List<PlayerAuth> getAllAuths();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last ten players who have recently logged in (first ten players with highest last login date).
|
||||||
|
*
|
||||||
|
* @return the 10 last players who last logged in
|
||||||
|
*/
|
||||||
|
List<PlayerAuth> getRecentlyLoggedInPlayers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload the data source.
|
* Reload the data source.
|
||||||
*/
|
*/
|
||||||
|
@ -393,6 +393,11 @@ public class FlatFile implements DataSource {
|
|||||||
throw new UnsupportedOperationException("Flat file no longer supported");
|
throw new UnsupportedOperationException("Flat file no longer supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
|
||||||
|
throw new UnsupportedOperationException("Flat file no longer supported");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a PlayerAuth object from the read data.
|
* Creates a PlayerAuth object from the read data.
|
||||||
*
|
*
|
||||||
|
@ -716,6 +716,22 @@ public class MySQL implements DataSource {
|
|||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
|
||||||
|
List<PlayerAuth> players = new ArrayList<>();
|
||||||
|
String sql = "SELECT * FROM " + tableName + " ORDER BY " + col.LAST_LOGIN + " DESC LIMIT 10;";
|
||||||
|
try (Connection con = getConnection();
|
||||||
|
Statement st = con.createStatement();
|
||||||
|
ResultSet rs = st.executeQuery(sql)) {
|
||||||
|
while (rs.next()) {
|
||||||
|
players.add(buildAuthFromResultSet(rs));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logSqlException(e);
|
||||||
|
}
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
|
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
|
||||||
String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT);
|
String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT);
|
||||||
int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP);
|
int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP);
|
||||||
|
@ -639,6 +639,20 @@ public class SQLite implements DataSource {
|
|||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
|
||||||
|
List<PlayerAuth> players = new ArrayList<>();
|
||||||
|
String sql = "SELECT * FROM " + tableName + " ORDER BY " + col.LAST_LOGIN + " DESC LIMIT 10;";
|
||||||
|
try (Statement st = con.createStatement(); ResultSet rs = st.executeQuery(sql)) {
|
||||||
|
while (rs.next()) {
|
||||||
|
players.add(buildAuthFromResultSet(rs));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logSqlException(e);
|
||||||
|
}
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
|
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
|
||||||
String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null;
|
String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null;
|
||||||
|
|
||||||
|
@ -50,6 +50,11 @@ public enum AdminPermission implements PermissionNode {
|
|||||||
*/
|
*/
|
||||||
GET_IP("authme.admin.getip"),
|
GET_IP("authme.admin.getip"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Administrator command to see the last recently logged in players.
|
||||||
|
*/
|
||||||
|
SEE_RECENT_PLAYERS("authme.admin.seerecent"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Administrator command to teleport to the AuthMe spawn.
|
* Administrator command to teleport to the AuthMe spawn.
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,7 @@ softdepend:
|
|||||||
commands:
|
commands:
|
||||||
authme:
|
authme:
|
||||||
description: AuthMe op commands
|
description: AuthMe op commands
|
||||||
usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|debug
|
usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug
|
||||||
email:
|
email:
|
||||||
description: Add email or recover password
|
description: Add email or recover password
|
||||||
usage: /email show|add|change|recover|code|setpassword
|
usage: /email show|add|change|recover|code|setpassword
|
||||||
@ -74,6 +74,7 @@ permissions:
|
|||||||
authme.admin.register: true
|
authme.admin.register: true
|
||||||
authme.admin.reload: true
|
authme.admin.reload: true
|
||||||
authme.admin.seeotheraccounts: true
|
authme.admin.seeotheraccounts: true
|
||||||
|
authme.admin.seerecent: true
|
||||||
authme.admin.setfirstspawn: true
|
authme.admin.setfirstspawn: true
|
||||||
authme.admin.setspawn: true
|
authme.admin.setspawn: true
|
||||||
authme.admin.spawn: true
|
authme.admin.spawn: true
|
||||||
@ -134,6 +135,9 @@ permissions:
|
|||||||
authme.admin.seeotheraccounts:
|
authme.admin.seeotheraccounts:
|
||||||
description: Permission to see the other accounts of the players that log in.
|
description: Permission to see the other accounts of the players that log in.
|
||||||
default: op
|
default: op
|
||||||
|
authme.admin.seerecent:
|
||||||
|
description: Administrator command to see the last recently logged in players.
|
||||||
|
default: op
|
||||||
authme.admin.setfirstspawn:
|
authme.admin.setfirstspawn:
|
||||||
description: Administrator command to set the first AuthMe spawn.
|
description: Administrator command to set the first AuthMe spawn.
|
||||||
default: op
|
default: op
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
package fr.xephi.authme.command.executable.authme;
|
||||||
|
|
||||||
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
|
import fr.xephi.authme.datasource.DataSource;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link RecentPlayersCommand}.
|
||||||
|
*/
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class RecentPlayersCommandTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
@Spy
|
||||||
|
private RecentPlayersCommand command;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldShowRecentPlayers() {
|
||||||
|
// given
|
||||||
|
PlayerAuth auth1 = PlayerAuth.builder()
|
||||||
|
.name("hannah").realName("Hannah").lastIp("11.11.11.11")
|
||||||
|
.lastLogin(1510387755000L) // 11/11/2017 @ 8:09am
|
||||||
|
.build();
|
||||||
|
PlayerAuth auth2 = PlayerAuth.builder()
|
||||||
|
.name("matt").realName("MATT").lastIp("22.11.22.33")
|
||||||
|
.lastLogin(1510269301000L) // 11/09/2017 @ 11:15pm
|
||||||
|
.build();
|
||||||
|
doReturn(ZoneId.of("UTC")).when(command).getZoneId();
|
||||||
|
given(dataSource.getRecentlyLoggedInPlayers()).willReturn(Arrays.asList(auth1, auth2));
|
||||||
|
|
||||||
|
CommandSender sender = mock(CommandSender.class);
|
||||||
|
|
||||||
|
// when
|
||||||
|
command.executeCommand(sender, Collections.emptyList());
|
||||||
|
|
||||||
|
// then
|
||||||
|
verify(sender).sendMessage(argThat(containsString("Recently logged in players")));
|
||||||
|
verify(sender).sendMessage("- Hannah (08:09 AM, 11 Nov with IP 11.11.11.11)");
|
||||||
|
verify(sender).sendMessage("- MATT (11:15 PM, 09 Nov with IP 22.11.22.33)");
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package fr.xephi.authme.datasource;
|
package fr.xephi.authme.datasource;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -467,4 +468,30 @@ public abstract class AbstractDataSourceIntegrationTest {
|
|||||||
assertThat(dataSource.hasSession("user"), equalTo(true));
|
assertThat(dataSource.hasSession("user"), equalTo(true));
|
||||||
assertThat(dataSource.hasSession("nonExistentName"), equalTo(false));
|
assertThat(dataSource.hasSession("nonExistentName"), equalTo(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldGetRecentlyLoggedInPlayers() {
|
||||||
|
// given
|
||||||
|
DataSource dataSource = getDataSource();
|
||||||
|
String[] names = {"user3", "user8", "user2", "user4", "user7",
|
||||||
|
"user11", "user14", "user12", "user18", "user16",
|
||||||
|
"user28", "user29", "user22", "user20", "user24"};
|
||||||
|
long timestamp = 1461024000; // 2016-04-19 00:00:00
|
||||||
|
for (int i = 0; i < names.length; ++i) {
|
||||||
|
PlayerAuth auth = PlayerAuth.builder().name(names[i])
|
||||||
|
.registrationDate(1234567)
|
||||||
|
.lastLogin(timestamp + i * 3600)
|
||||||
|
.build();
|
||||||
|
dataSource.saveAuth(auth);
|
||||||
|
dataSource.updateSession(auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
List<PlayerAuth> recentPlayers = dataSource.getRecentlyLoggedInPlayers();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(Lists.transform(recentPlayers, PlayerAuth::getNickname),
|
||||||
|
contains("user24", "user20", "user22", "user29", "user28",
|
||||||
|
"user16", "user18", "user12", "user14", "user11"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user