I tried to create a function that creates a category upon certain conditions.
It should create a category if there are no categories called "Server Information"
Code:
public class EventListener extends ListenerAdapter {
//Checks if target category exists, if it is - do nothing, and if it does not - create one
String categoryInfoName = "Server Information";
#Override
public void onGenericGuild(GenericGuildEvent event) {
try {
if (!event.getGuild().getCategories().stream().anyMatch(category -> category.getName().equals(categoryInfoName))) {
event.getGuild().createCategory(categoryInfoName);
}
}
catch (InsufficientPermissionException permissionException)
{
permissionException.printStackTrace();
}
}
No matter what I change in Discord's server settings it still gives me these errors:
net.dv8tion.jda.api.exceptions.InsufficientPermissionException: Cannot perform action due to a lack of Permission. Missing permission: MANAGE_CHANNEL
at net.dv8tion.jda.internal.entities.GuildImpl.checkPermission(GuildImpl.java:1898)
at net.dv8tion.jda.internal.entities.GuildImpl.checkCanCreateChannel(GuildImpl.java:1934)
at net.dv8tion.jda.internal.entities.GuildImpl.createChannel(GuildImpl.java:1729)
at net.dv8tion.jda.internal.entities.GuildImpl.createCategory(GuildImpl.java:1724)
at me.izmike.jdabot.listeners.EventListener.onGenericGuild(EventListener.java:22)
at net.dv8tion.jda.api.hooks.ListenerAdapter.onEvent(ListenerAdapter.java:424)
at net.dv8tion.jda.api.hooks.InterfacedEventManager.handle(InterfacedEventManager.java:96)
at net.dv8tion.jda.internal.hooks.EventManagerProxy.handleInternally(EventManagerProxy.java:88)
at net.dv8tion.jda.internal.hooks.EventManagerProxy.handle(EventManagerProxy.java:70)
at net.dv8tion.jda.internal.JDAImpl.handleEvent(JDAImpl.java:171)
at net.dv8tion.jda.internal.handle.GuildSetupNode.completeSetup(GuildSetupNode.java:376)
at net.dv8tion.jda.internal.handle.GuildSetupNode.handleMemberChunk(GuildSetupNode.java:260)
How can I fix this?
Related
My question is how can I get the selected option, if in the SelectionMenuEvent event it doesn't have the method to get the option that the user selected from the menu.
My code:
#Override
public void execute(Member member, Message message) {
val menu = SelectionMenu.create("help.menu");
val command = this.loadAllCommand(message);
command.forEach(c -> menu.addOption(c.getName(), c.getPermission(), c.getDescription(), c.getEmoji()));
message.reply("```Diff\n" +
"+ List of my current commands" +
"\n```").setActionRow(menu.build()).queue();
}
// Event
#Override
public void onSelectionMenu(SelectionMenuEvent event) {
// get selected option of the menu
}
I looked at the SelectionMenuEvent class documentation, let me know if I missed something.
I am using JDA version 4.4.0_350.
You can use SelectMenuInteraction#getValues or SelectMenuInteraction#getSelectedOptions. The event implements this interface.
//import
#EventBusSubscriber
public class RegistryHandler {
#SubscribeEvent
public static void onItemRegister(RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(ItemInit.ITEMS.toArray(new Item[0]));
}
#SubscribeEvent
public static void onBlockRegister(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll((Block[]) BlockInit.BLOCKS.toArray());
}
public static void onModelRegister(ModelRegistryEvent event) {
for (Item item : ItemInit.ITEMS) {
if (item instanceof IHasModel) {
((IHasModel)item).registerModels();
}
}
for (Block block : BlockInit.BLOCKS) {
if (block instanceof IHasModel) {
((IHasModel)block).registerModels();
}
}
}
}
So, i followed Harry Talks tutorials and tried to build the mod. It doesn't work for some unknown reason.
Error Log
Project code on github
Here's the important part of your error log:
java.lang.IllegalStateException: Attempted to set registry name with existing registry name! New: fur:decrafting_table Old: fur:decrafting_table
at net.minecraftforge.registries.IForgeRegistryEntry$Impl.setRegistryName(IForgeRegistryEntry.java:71)
at net.minecraftforge.registries.IForgeRegistryEntry$Impl.setRegistryName(IForgeRegistryEntry.java:79)
at com.furnacelztmod.blocks.BlockBase.<init>(BlockBase.java:23)
The problem is here:
public BlockBase(String name, Material material) {
super(material);
setUnlocalizedName(name);
setRegistryName(name);
setCreativeTab(CreativeTabs.BUILDING_BLOCKS);
BlockInit.BLOCKS.add(this);
Block iter=this;
iter.setRegistryName(iter.getRegistryName()); // line 23
ItemInit.ITEMS.add(new ItemBlock(iter));
}
Specifically, you shouldn't be doing iter.setRegistryName(iter.getRegistryName());. Forge doesn't let you do that, and even if it did, it would be a no-op, just setting the registry name to what it already was. I suspect you actually meant to set the registry name on the ItemBlock there, which you should be able to do by replacing the last three lines of that constructor with this:
ItemBlock iter=new ItemBlock(this);
iter.setRegistryName(this.getRegistryName());
ItemInit.ITEMS.add(iter);
I have not been able to find a solution on google or on stackoverflow for my scenario and I am stuck.
For the automation testing I am using InteliJ (as IDE), java, Selenium, Appium and TestNG.
I have actions performed on the website to initialize the mobile, and after that the automation perform the actions on the mobile.
The screenshot when the test fails captures the website and the mobile screen.
I need to capture only the screen related to the failed test action.
Please see the code:
public abstract class BaseTest implements ITest, V3RestApi, V2Api {
private boolean isMobileAppLaunched = false;
#AfterMethod
public void afterMainMethod(ITestResult result) {
try {
if (result.getStatus() == ITestResult.FAILURE) {
captureScreenshot(result);
}
driver.quit();
if (isMobileAppLaunched) {
this.closeAppiumSession();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void captureScreenshot(ITestResult result) {
try {
String screenshotName;
File screenshot;
screenshotName = Utilities.getFileName(result.getName());
screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
this.attachScreenShotWithReport(screenshotName, screenshot, result);
if (isMobileAppLaunched) {
screenshotName = Utilities.getFileName(result.getName());
screenshot = ((TakesScreenshot) appiumDriver).getScreenshotAs(OutputType.FILE);
this.attachScreenShotWithReport(screenshotName, screenshot, result);
}
} catch (Exception e) {
logger.warn("Screenshot could not be captured for " + result.getName());
}
}
public void launchMobileApplication(MobileType mobileApplicationType) throws Exception {
this.isMobileAppLaunched = true;
}
}
public class AndroidTestCase extends BaseTest {
#Test(description = "Test description"})
public void testCaseOnAndroid() throws Exception {
reportLog("Login into the application as User Name");
//login action to website;
reportLog("Click on Hamburger Menu");
//click action on the website;
reportLog("Activate to recognize the mobile"));
//action on site to recognize the mobile;
reportLog("Mobile: Launch Mobile Application");
//launch the mobile;
reportLog("Mobile: Login into the Mobile application as User Name");
//action to login;
reportLog("Mobile: Click on tab");
//action on Mobile;
}
}
With the assumption that you guys differentiate mobile actions from web actions by logging a message prefixed with "Mobile:" and that the reportLog method is always invoked within the same thread as the test method itself (e.g., testCaseOnAndroid), we can build a cache that holds the last attempted action for a given thread (test case) whenever reportLog is invoked. If a test case fails and afterTestCase is called, we can inspect the cache and get the last attempted action for the current thread (the method annotated with #AfterMethod is normally called in the same thread as the test method itself), based on which, we can now decide whether we need to call the driver that captures a screenshot of the browser window or the driver that captures a screenshot of the emulated device's screen:
public abstract class BaseTest {
/**
* Defines the type of a reported test action.
*/
public enum ReportedActionType {
MOBILE,
WEB
}
private final ConcurrentHashMap<Long, ReportedActionType> lastAttemptedActionCache = new ConcurrentHashMap<>();
#AfterMethod
public void afterTestCase(final ITestResult testResult) {
final Long currentThreadId = currentThread().getId();
final ReportedActionType lastReportedActionType = this.lastAttemptedActionCache.get(currentThreadId);
if (testResult.getStatus() == FAILURE) {
printToConsole(String.format("Test failed while attempting to perform a '%1$s' action. | %2$s",
lastReportedActionType,
testResult.getName()));
try {
if (lastReportedActionType == MOBILE) {
captureEmulatedMobileDevice(testResult);
} else {
captureBrowserWindow(testResult);
}
} catch (final Exception exception) {
exception.printStackTrace();
}
}
// todo: quit web driver (Selenium)
// todo: quit mobile driver (close Appium session)
// irrespective of the state of the test result (success or failure), we need to make sure that we
// remove the cached information, otherwise the cache can get really
// large and this could lead to out of memory problems (we could potentially consider
// using a more sophisticated cache implementation of a 3rd-party library
// that supports time-based eviction, so that even if we forget to remove the
// cached information manually, it gets removed automatically after a fixed amount of time - e.g., 5-10 seconds)
this.lastAttemptedActionCache.remove(currentThreadId);
}
// todo: call the appropriate driver to capture a screenshot of the emulated device
private void captureEmulatedMobileDevice(final ITestResult testResult) {
printToConsole("Screenshot of the emulated mobile device has been captured. | " + testResult.getName());
}
// todo: call the appropriate driver to capture a screenshot of the browser window
private void captureBrowserWindow(final ITestResult testResult) {
printToConsole("Screenshot of the browser has been captured. | " + testResult.getName());
}
public void reportLog(final String message) {
// log the message (to console, to file, etc.)
printToConsole(message);
// the assumption is that the actions within a test case are executed within the same
// thread the test case itself is executed in; as long as this assumption holds, we can cache
// the needed information and fetch it later to perform the needed checks
this.lastAttemptedActionCache.put(currentThread().getId(),
getReportedActionType(message));
}
private ReportedActionType getReportedActionType(final String reportLogMessage) {
return reportLogMessage.toLowerCase()
.trim()
.startsWith("mobile:") ? MOBILE : WEB;
}
// todo: replace this with a proper logger
private void printToConsole(final String message) {
System.out.println(currentThread().getId() + " | " + this.getClass()
.getSimpleName() + " | " + message);
}
}
A more proper solution would very likely require changing hundreds/thousands of tests (which is very likely not desired). Ideally, test case steps (actions) should be modelled more properly and not only exist in our imagination as "things" separated by reportLog method calls.
Java is an OOP language after all.
I would like to know
Am I doing things (the following) too complicated?
Is there a better way to update the main content of an activity that allows me to bookmark the event calendar of a store via URL like #MainPlace:eventCalendar?storeId=<id>?
I'm having this ActivityMapper here
public class AppActivityMapper implements ActivityMapper {
private ClientFactory clientFactory;
private MainActivity mainActivity;
// ..
#Override
public Activity getActivity(Place place) {
if (place instanceof LoginPlace) {
return new LoginActivity((LoginPlace) place, clientFactory);
} else if (place instanceof MainPlace) {
if(this.mainActivity == null) {
this.mainActivity = new MainActivity((MainPlace) place, clientFactory);
} else {
this.mainActivity.updateMainContent(((MainPlace) place).getMainContentToken());
}
return this.mainActivity;
}
return null;
}
}
and a MainActivity that controls my MainView that is just a menu ond the left side and the main content on the right side.
I want to decouple my views like in Best Practices for Architecting GWT App which is why I'm trying to control the main content by using events that get fired as something gets clicked in my MenuView.
Therefore I am initializing some event handlers in my MainActivity that react to clicks on the buttons in my menu to delegate the update to the MainView.
public class MainActivity extends AbstractActivity implements MainView.MainPresenter {
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
this.mainView = this.clientFactory.getMainView();
this.mainView.setPresenter(this);
this.mainView.initialize();
this.eventBus = eventBus;
this.eventBus.addHandler(HomeClickedEvent.TYPE, new HomeClickedHandler() {
#Override
public void onHomeClicked(HomeClickedEvent event) {
goTo(new MainPlace("home"));
}
});
this.eventBus.addHandler(EventCalendarClickedEvent.TYPE, new EventCalendarClickedHandler() {
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
});
panel.setWidget(this.mainView.asWidget());
}
#Override
public void goTo(Place place) {
this.clientFactory.getPlaceController().goTo(place);
}
#Override
public void updateMainContent(String currentMainContentToken) {
this.mainView.updateMainContent(currentMainContentToken);
}
}
this event gets fired by MenuPresenter.clickedEventCalendar() that reacts to a click on the corresponding menu entry of the MenuView:
public class MenuPresenter implements MenuView.MenuPresenter {
// ..
#Override
public void clickedEventCalendar(Long storeId) {
this.eventBus.fireEvent(new EventCalendarClickedEvent(storeId));
}
}
One of the things I really don't like is this where I append parameters to the token e.g. to display the event calendar of a store given by storeId:
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
is there a cleaner solution for a problem like this in GWT? I don't like the fact that I'd have to parse that string in my actual event calendar. Am I using the ActivityMapper wrong or is there simply no other way to do this?
This question should really be split into several separate ones, but that's maybe something to keep in mind for the future. If you're asking one thing then it's easier to answer thoroughly and others can find the answer easier too.
Anyway, I can see a few improvements:
use EventBinder to get rid a bit of the cruft when handling and creating new events.
if you just want to let the presenter know that a button was pressed on in the view (associated with that presenter) sending a custom event over the event bus is a bit of an overkill. Depending on your needs you can expose the button in your view's interface:
public interface Display {
HasClickHandlers getButton();
}
And then just register the ClickHandler in your presenter.
Or, if you need to do something view- and presenter- related on the click, register the ClickHandler in your view and call the presenter:
// In MainView:
#UiHandler("button")
void handleClick(ClickEvent event) {
// Do some stuff with view,
// like hide a panel or change colour
panel.setVisible(false);
// Let the presenter know that a click event has been fired
presenter.onEventCalendarClicked();
}
you're right - creating MainPlace like you are proposing is wrong. You are creating the token too soon - that's what the tokenizer associated with the place is for. You should create MainPlace by passing just the storeId to the constructor - why should MainPresenter (or any other class using this place) should know how to create the token? MainPlace should look more like this:
public class MainPlace extends Place {
private final Long storeId;
public MainPlace(Long storeId) {
this.storeId = storeId;
}
public Long getStoreId() {
return storeId;
}
public static class Tokenizer implements PlaceTokenizer<MainPlace> {
#Override
public MainPlace getPlace(String token) {
return new MainPlace(Long.valueOf(token));
}
#Override
public String getToken(MainPlace place) {
return "eventCalendar?storeId=" + place.getStoreId();
}
}
}
Now, it's the Tokenizer's responisibily to create and parse the token. Just remember to register it on your PlaceHistoryMapper.
I am using Eclipse 4.2 Juno, Java 1.6. I have two parts in my application. One part is registering the SelectionChangedListener:
#Inject
private ESelectionService selectionService;
#PostConstruct
public void init() {
TreeViewer bsTreeViewer = new TreeViewer(tabFolder, SWT.BORDER);
/* some other stuff */
// Event declaration
bsTreeViewer.addSelectionChangedListener(new SelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
if( selectionService != null ) {
selectionService.setSelection(((IStructuredSelection)event.getSelection()).getFirstElement());
}
}
});
}
This Listener is called correctly. The first selected Element is of the right type, too.
I another part I am setting up the receiving end:
#Inject
public void setBS(#Named(IServiceConstants.ACTIVE_SELECTION) #Optional BS bs) {
if (bs == null) {
/* implementation not shown */
} else {
/* implementation not shown */
}
}
However, nothing is received on this end of the pipe. What am I doing wrong or how could I debug this?
The code above looks fine, but try to check the following issues:
check if the receiver object is created - if not, it won't receive an event
check if the receiver object is created by eclipse framework (for example if it is element of application model such as part, handler it is for sure created by the framework) - if not, the framework (selection service) does not know about the receiver object and cannot notify it